aiobungie

A Pythonic async/await wrapper for interacting with the Bungie API.

Base client.

Example
import aiobungie

client = aiobungie.Client('YOUR_API_KEY')

# Search for Destiny2 users.
async def main() -> None:
    async with client.rest:
        users = await client.search_users('Crit')

        # Iterate over the users and take the first 5 results.
        for user in users.take(5):
            print(f'{user.name} ({user.code})')

            # Iterate through the users memberships.
            for membership in user.memberships:
                print(membership.type, membership.id)

client.run(main()) # or asyncio.run(main())

Single RESTClient instance.

The difference between base client and the REST clients:

  • No Hight-Level concepts.
  • All returned data are pure JSON objects from the API.
  • No object creation.
Example
import aiobungie

async def main() -> None:
    # Using `async with` context manager to close the session properly.
    async with aiobungie.RESTClient("TOKEN") as rest:
        payload = await rest.fetch_player('Fate怒', 4275)

        for membership in payload:
            print(membership['membershipId'], membership['iconPath'])

import asyncio
asyncio.run(main())

REST client pool.

A REST client pool allows you to acquire multiple RESTClient instances that shares the same connection.

Example
import aiobungie
import asyncio

pool = aiobungie.RESTPool("token")

async def func1() -> None:
    async with pool.acquire() as instance:
        tokens = await instance.fetch_oauth2_tokens('code')
        pool.metadata['tokens'] = tokens

# Other instance may access the tokens from pool since its shared.

async def func2() -> None:
    async with pool.acquire() as instance:
        tokens = pool.metadata['tokens']
        tokens = await instance.refresh_access_token(tokens.refresh_token)

async def main() -> None:
    await asyncio.gather(func1(), func2())

asyncio.run(main())

Should you use the base client or the REST client? This returns to you. For an example if you're building a website.

You can use python as a REST API in the backend with the RESTClient since all returned object are JSON objects. Which gives you the freedom to deserialize it and implement your own logic in the front-end.

Or of you're building a Discord bot for an example or something simple. The base client is the way to go.

  1# MIT License
  2#
  3# Copyright (c) 2020 - Present nxtlo
  4#
  5# Permission is hereby granted, free of charge, to any person obtaining a copy
  6# of this software and associated documentation files (the "Software"), to deal
  7# in the Software without restriction, including without limitation the rights
  8# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9# copies of the Software, and to permit persons to whom the Software is
 10# furnished to do so, subject to the following conditions:
 11#
 12# The above copyright notice and this permission notice shall be included in all
 13# copies or substantial portions of the Software.
 14#
 15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 18# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 20# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 21# SOFTWARE.
 22
 23"""A Pythonic `async`/`await` wrapper for interacting with the Bungie API.
 24
 25Base client.
 26
 27Example
 28-------
 29```py
 30import aiobungie
 31
 32client = aiobungie.Client('YOUR_API_KEY')
 33
 34# Search for Destiny2 users.
 35async def main() -> None:
 36    async with client.rest:
 37        users = await client.search_users('Crit')
 38
 39        # Iterate over the users and take the first 5 results.
 40        for user in users.take(5):
 41            print(f'{user.name} ({user.code})')
 42
 43            # Iterate through the users memberships.
 44            for membership in user.memberships:
 45                print(membership.type, membership.id)
 46
 47client.run(main()) # or asyncio.run(main())
 48```
 49
 50Single RESTClient instance.
 51
 52The difference between base client and the REST clients:
 53
 54* No Hight-Level concepts.
 55* All returned data are pure JSON objects from the API.
 56* No object creation.
 57
 58Example
 59-------
 60```py
 61import aiobungie
 62
 63async def main() -> None:
 64    # Using `async with` context manager to close the session properly.
 65    async with aiobungie.RESTClient("TOKEN") as rest:
 66        payload = await rest.fetch_player('Fate怒', 4275)
 67
 68        for membership in payload:
 69            print(membership['membershipId'], membership['iconPath'])
 70
 71import asyncio
 72asyncio.run(main())
 73```
 74
 75REST client pool.
 76
 77A REST client pool allows you to acquire multiple `RESTClient` instances that shares the same connection.
 78
 79Example
 80-------
 81```py
 82import aiobungie
 83import asyncio
 84
 85pool = aiobungie.RESTPool("token")
 86
 87async def func1() -> None:
 88    async with pool.acquire() as instance:
 89        tokens = await instance.fetch_oauth2_tokens('code')
 90        pool.metadata['tokens'] = tokens
 91
 92# Other instance may access the tokens from pool since its shared.
 93
 94async def func2() -> None:
 95    async with pool.acquire() as instance:
 96        tokens = pool.metadata['tokens']
 97        tokens = await instance.refresh_access_token(tokens.refresh_token)
 98
 99async def main() -> None:
100    await asyncio.gather(func1(), func2())
101
102asyncio.run(main())
103```
104
105Should you use the base client or the REST client?
106This returns to you. For an example if you're building a website.
107
108You can use python as a REST API in the backend with the RESTClient since all returned object are JSON objects.
109Which gives you the freedom to deserialize it and implement your own logic in the front-end.
110
111Or of you're building a Discord bot for an example or something simple. The base client is the way to go.
112"""
113
114
115from __future__ import annotations
116
117from aiobungie import builders
118from aiobungie import crates
119from aiobungie import interfaces
120from aiobungie import traits
121from aiobungie import typedefs
122from aiobungie import url
123from aiobungie.client import Client
124from aiobungie.error import *
125from aiobungie.internal import iterators
126from aiobungie.internal.assets import Image
127from aiobungie.internal.enums import *
128from aiobungie.internal.factory import Factory
129from aiobungie.internal.iterators import *
130from aiobungie.rest import *
131from aiobungie.undefined import UNDEFINED
132from aiobungie.undefined import UndefinedOr
133from aiobungie.undefined import UndefinedType
134
135from ._info import __about__
136from ._info import __author__
137from ._info import __docs__
138from ._info import __email__
139from ._info import __license__
140from ._info import __url__
141from ._info import __version__
142
143# Alias for crate for backwards compatibility.
144crate = crates
145
146# Activity enums
147from .crates.activity import Difficulty
148
149# Components enums
150from .crates.components import ComponentFields
151from .crates.components import ComponentPrivacy
152
153# Entity enums
154from .crates.entity import GatingScope
155from .crates.entity import ObjectiveUIStyle
156from .crates.entity import ValueUIStyle
157
158# Fireteam enums.
159from .crates.fireteams import FireteamActivity
160from .crates.fireteams import FireteamDate
161from .crates.fireteams import FireteamLanguage
162from .crates.fireteams import FireteamPlatform
163
164# Records enums
165from .crates.records import RecordState
166
167__all__ = [mod for mod in dir() if not mod.startswith("_")]  # type: ignore
@attrs.define(auto_exc=True)
class AiobungieError(builtins.RuntimeError):
57@attrs.define(auto_exc=True)
58class AiobungieError(RuntimeError):
59    """Base exception class that all other errors inherit from."""

Base exception class that all other errors inherit from.

AiobungieError()
2def __init__(self, ):
3    BaseException.__init__(self, )

Method generated by attrs for class AiobungieError.

Inherited Members
builtins.BaseException
with_traceback
@typing.final
class AmmoType(builtins.int, aiobungie.Enum):
643@typing.final
644class AmmoType(int, Enum):
645    """AN enum for Detyiny 2 ammo types."""
646
647    NONE = 0
648    PRIMARY = 1
649    SPECIAL = 2
650    HEAVY = 3

AN enum for Detyiny 2 ammo types.

NONE = <AmmoType.NONE: 0>
PRIMARY = <AmmoType.PRIMARY: 1>
SPECIAL = <AmmoType.SPECIAL: 2>
HEAVY = <AmmoType.HEAVY: 3>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@attrs.define(auto_exc=True)
class BadRequest(aiobungie.HTTPError):
147@attrs.define(auto_exc=True)
148class BadRequest(HTTPError):
149    """Bad requests exceptions."""
150
151    url: typing.Optional[typedefs.StrOrURL]
152    """The URL/endpoint caused this error."""
153
154    body: typing.Any
155    """The response body."""
156
157    headers: multidict.CIMultiDictProxy[str]
158    """The response headers."""
159
160    http_status: http.HTTPStatus = attrs.field(default=http.HTTPStatus.BAD_REQUEST)

Bad requests exceptions.

BadRequest( message: str, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str], http_status: http.HTTPStatus = <HTTPStatus.BAD_REQUEST: 400>)
2def __init__(self, message, url, body, headers, http_status=attr_dict['http_status'].default):
3    self.message = message
4    self.url = url
5    self.body = body
6    self.headers = headers
7    self.http_status = http_status
8    BaseException.__init__(self, self.message,self.url,self.body,self.headers,self.http_status)

Method generated by attrs for class BadRequest.

url: Union[str, yarl.URL, NoneType]

The URL/endpoint caused this error.

body: Any

The response body.

headers: multidict._multidict.CIMultiDictProxy[str]

The response headers.

http_status: http.HTTPStatus

The response status.

Inherited Members
HTTPError
message
builtins.BaseException
with_traceback
@typing.final
class ClanMemberType(builtins.int, aiobungie.Enum):
698@typing.final
699class ClanMemberType(int, Enum):
700    """An enum for bungie clan member types."""
701
702    NONE = 0
703    BEGINNER = 1
704    MEMBER = 2
705    ADMIN = 3
706    ACTING_FOUNDER = 4
707    FOUNDER = 5

An enum for bungie clan member types.

NONE = <ClanMemberType.NONE: 0>
BEGINNER = <ClanMemberType.BEGINNER: 1>
MEMBER = <ClanMemberType.MEMBER: 2>
ADMIN = <ClanMemberType.ADMIN: 3>
ACTING_FOUNDER = <ClanMemberType.ACTING_FOUNDER: 4>
FOUNDER = <ClanMemberType.FOUNDER: 5>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Class(builtins.int, aiobungie.Enum):
474@typing.final
475class Class(int, Enum):
476    """An Enum for Destiny character classes."""
477
478    TITAN = 0
479    HUNTER = 1
480    WARLOCK = 2
481    UNKNOWN = 3

An Enum for Destiny character classes.

TITAN = <Class.TITAN: 0>
HUNTER = <Class.HUNTER: 1>
WARLOCK = <Class.WARLOCK: 2>
UNKNOWN = <Class.UNKNOWN: 3>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
class Client(aiobungie.traits.ClientApp):
  61class Client(traits.ClientApp):
  62    """Standard Bungie API client application.
  63
  64    This client deserialize the REST JSON responses using `aiobungie.internal.factory.Factory`
  65    and returns `aiobungie.crates` Python object implementations of the responses.
  66
  67    A `aiobungie.RESTClient` REST client can also be used alone for low-level concepts.
  68
  69    Example
  70    -------
  71    ```py
  72    import aiobungie
  73
  74    client = aiobungie.Client('...')
  75
  76    async def main():
  77        async with client.rest:
  78            user = await client.fetch_current_user_memberships('...')
  79            print(user)
  80    ```
  81
  82    Parameters
  83    -----------
  84    token: `str`
  85        Your Bungie's API key or Token from the developer's portal.
  86
  87    Other Parameters
  88    ----------------
  89    rest_client: `aiobungie.interfaces.RESTInterface | None`
  90        An optional rest client instance you can pass.
  91        If set to `None` then the client will use the default instance.
  92
  93    max_retries : `int`
  94        The max retries number to retry if the request hit a `5xx` status code.
  95    max_ratelimit_retries : `int`
  96        The max retries number to retry if the request hit a `429` status code. Defaults to `3`.
  97    client_secret : `str | None`
  98        An optional application client secret,
  99        This is only needed if you're fetching OAuth2 tokens with this client.
 100    client_id : `int | None`
 101        An optional application client id,
 102        This is only needed if you're fetching OAuth2 tokens with this client.
 103    """
 104
 105    __slots__ = ("_rest", "_factory", "_client_secret", "_client_id")
 106
 107    def __init__(
 108        self,
 109        token: str,
 110        /,
 111        client_secret: typing.Optional[str] = None,
 112        client_id: typing.Optional[int] = None,
 113        *,
 114        rest_client: typing.Optional[interfaces.RESTInterface] = None,
 115        max_retries: int = 4,
 116        max_ratelimit_retries: int = 3,
 117    ) -> None:
 118
 119        self._client_secret = client_secret
 120        self._client_id = client_id
 121
 122        self._rest = (
 123            rest_client
 124            if rest_client is not None
 125            else rest_.RESTClient(
 126                token,
 127                client_secret,
 128                client_id,
 129                max_retries=max_retries,
 130                max_ratelimit_retries=max_ratelimit_retries,
 131            )
 132        )
 133
 134        self._factory = factory_.Factory(self)
 135
 136    @property
 137    def factory(self) -> factory_.Factory:
 138        return self._factory
 139
 140    @property
 141    def rest(self) -> interfaces.RESTInterface:
 142        return self._rest
 143
 144    @property
 145    def request(self) -> Client:
 146        return copy.copy(self)
 147
 148    @property
 149    def metadata(self) -> collections.MutableMapping[typing.Any, typing.Any]:
 150        return self._rest.metadata
 151
 152    def run(
 153        self, future: collections.Coroutine[typing.Any, None, None], debug: bool = False
 154    ) -> None:
 155        loop: typing.Final[asyncio.AbstractEventLoop] = helpers.get_or_make_loop()
 156        try:
 157            if not loop.is_running():
 158                loop.set_debug(debug)
 159                loop.run_until_complete(future)
 160
 161        except Exception as exc:
 162            raise RuntimeError(f"Failed to run {future.__qualname__}") from exc
 163
 164        except KeyboardInterrupt:
 165            _LOG.warn("Unexpected Keyboard interrupt. Exiting.")
 166            return
 167
 168    # * User methods.
 169
 170    async def fetch_current_user_memberships(self, access_token: str, /) -> user.User:
 171        """Fetch and return a user object of the bungie net user associated with account.
 172
 173        .. warning::
 174            This method requires OAuth2 scope and a Bearer access token.
 175
 176        Parameters
 177        ----------
 178        access_token : `str`
 179            A valid Bearer access token for the authorization.
 180
 181        Returns
 182        -------
 183        `aiobungie.crates.user.User`
 184            A user object includes the Destiny memberships and Bungie.net user.
 185        """
 186        resp = await self.rest.fetch_current_user_memberships(access_token)
 187
 188        return self.factory.deserialize_user(resp)
 189
 190    async def fetch_bungie_user(self, id: int, /) -> user.BungieUser:
 191        """Fetch a Bungie user by their BungieNet id.
 192
 193        .. note::
 194            This returns a Bungie user membership only. Take a look at `Client.fetch_membership_from_id`
 195            for other memberships.
 196
 197        Parameters
 198        ----------
 199        id: `int`
 200            The user id.
 201
 202        Returns
 203        -------
 204        `aiobungie.crates.user.BungieUser`
 205            A Bungie user.
 206
 207        Raises
 208        ------
 209        `aiobungie.error.NotFound`
 210            The user was not found.
 211        """
 212        payload = await self.rest.fetch_bungie_user(id)
 213
 214        return self.factory.deserialize_bungie_user(payload)
 215
 216    async def search_users(
 217        self, name: str, /
 218    ) -> iterators.Iterator[user.SearchableDestinyUser]:
 219        """Search for players and return all players that matches the same name.
 220
 221        Parameters
 222        ----------
 223        name : `buildins.str`
 224            The user name.
 225
 226        Returns
 227        -------
 228        `aiobungie.iterators.Iterator[aiobungie.crates.DestinyMembership]`
 229            A sequence of destiny memberships.
 230        """
 231        payload = await self.rest.search_users(name)
 232
 233        return iterators.Iterator(
 234            [
 235                self.factory.deserialize_searched_user(user)
 236                for user in payload["searchResults"]
 237            ]
 238        )
 239
 240    async def fetch_user_themes(self) -> collections.Sequence[user.UserThemes]:
 241        """Fetch all available user themes.
 242
 243        Returns
 244        -------
 245        `collections.Sequence[aiobungie.crates.user.UserThemes]`
 246            A sequence of user themes.
 247        """
 248        data = await self.rest.fetch_user_themes()
 249
 250        return self.factory.deserialize_user_themes(data)
 251
 252    async def fetch_hard_types(
 253        self,
 254        credential: int,
 255        type: typedefs.IntAnd[enums.CredentialType] = enums.CredentialType.STEAMID,
 256        /,
 257    ) -> user.HardLinkedMembership:
 258        """Gets any hard linked membership given a credential.
 259        Only works for credentials that are public just `aiobungie.CredentialType.STEAMID` right now.
 260        Cross Save aware.
 261
 262        Parameters
 263        ----------
 264        credential: `int`
 265            A valid SteamID64
 266        type: `aiobungie.CredentialType`
 267            The credential type. This must not be changed
 268            Since its only credential that works "currently"
 269
 270        Returns
 271        -------
 272        `aiobungie.crates.user.HardLinkedMembership`
 273            Information about the hard linked data.
 274        """
 275
 276        payload = await self.rest.fetch_hardlinked_credentials(credential, type)
 277
 278        return user.HardLinkedMembership(
 279            id=int(payload["membershipId"]),
 280            type=enums.MembershipType(payload["membershipType"]),
 281            cross_save_type=enums.MembershipType(payload["CrossSaveOverriddenType"]),
 282        )
 283
 284    async def fetch_membership_from_id(
 285        self,
 286        id: int,
 287        /,
 288        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.NONE,
 289    ) -> user.User:
 290        """Fetch Bungie user's memberships from their id.
 291
 292        Notes
 293        -----
 294        * This returns both BungieNet membership and a sequence of the player's DestinyMemberships
 295        Which includes Stadia, Xbox, Steam and PSN memberships if the player has them,
 296        see `aiobungie.crates.user.DestinyMembership` for more details.
 297        * If you only want the bungie user. Consider using `Client.fetch_user` method.
 298
 299        Parameters
 300        ----------
 301        id : `int`
 302            The user's id.
 303        type : `aiobungie.MembershipType`
 304            The user's membership type.
 305
 306        Returns
 307        -------
 308        `aiobungie.crates.User`
 309            A Bungie user with their membership types.
 310
 311        Raises
 312        ------
 313        aiobungie.NotFound
 314            The requested user was not found.
 315        """
 316        payload = await self.rest.fetch_membership_from_id(id, type)
 317
 318        return self.factory.deserialize_user(payload)
 319
 320    async def fetch_user_credentials(
 321        self, access_token: str, membership_id: int, /
 322    ) -> collections.Sequence[user.UserCredentials]:
 323        """Fetch an array of credential types attached to the requested account.
 324
 325        .. note::
 326            This method require OAuth2 Bearer access token.
 327
 328        Parameters
 329        ----------
 330        access_token : `str`
 331            The bearer access token associated with the bungie account.
 332        membership_id : `int`
 333            The id of the membership to return.
 334
 335        Returns
 336        -------
 337        `collections.Sequence[aiobungie.crates.UserCredentials]`
 338            A sequence of the attached user credentials.
 339
 340        Raises
 341        ------
 342        `aiobungie.Unauthorized`
 343            The access token was wrong or no access token passed.
 344        """
 345        resp = await self.rest.fetch_user_credentials(access_token, membership_id)
 346
 347        return self.factory.deserialize_user_credentials(resp)
 348
 349    # * Destiny 2.
 350
 351    async def fetch_profile(
 352        self,
 353        member_id: int,
 354        type: typedefs.IntAnd[enums.MembershipType],
 355        components: list[enums.ComponentType],
 356        auth: typing.Optional[str] = None,
 357    ) -> components.Component:
 358        """
 359        Fetch a bungie profile passing components to the request.
 360
 361        Parameters
 362        ----------
 363        member_id: `int`
 364            The member's id.
 365        type: `aiobungie.MembershipType`
 366            A valid membership type.
 367        components : `list[aiobungie.ComponentType]`
 368            List of profile components to collect and return.
 369
 370        Other Parameters
 371        ----------------
 372        auth : `typing.Optional[str]`
 373            A Bearer access_token to make the request with.
 374            This is optional and limited to components that only requires an Authorization token.
 375
 376        Returns
 377        --------
 378        `aiobungie.crates.Component`
 379            A Destiny 2 player profile with its components.
 380            Only passed components will be available if they exists. Otherwise they will be `None`
 381
 382        Raises
 383        ------
 384        `aiobungie.MembershipTypeError`
 385            The provided membership type was invalid.
 386        """
 387        data = await self.rest.fetch_profile(member_id, type, components, auth)
 388        return self.factory.deserialize_components(data)
 389
 390    async def fetch_linked_profiles(
 391        self,
 392        member_id: int,
 393        member_type: typedefs.IntAnd[enums.MembershipType],
 394        /,
 395        *,
 396        all: bool = False,
 397    ) -> profile.LinkedProfile:
 398        """Returns a summary information about all profiles linked to the requested member.
 399
 400        The passed membership id/type maybe a Bungie.Net membership or a Destiny memberships.
 401
 402        .. note::
 403            It will only return linked accounts whose linkages you are allowed to view.
 404
 405        Parameters
 406        ----------
 407        member_id : `int`
 408            The ID of the membership. This must be a valid Bungie.Net or PSN or Xbox ID.
 409        member_type : `aiobungie.MembershipType`
 410            The type for the membership whose linked Destiny account you want to return.
 411
 412        Other Parameters
 413        ----------------
 414        all : `bool`
 415            If provided and set to `True`, All memberships regardless
 416            of whether they're obscured by overrides will be returned,
 417
 418            If provided and set to `False`, Only available memberships will be returned.
 419            The default for this is `False`.
 420
 421        Returns
 422        -------
 423        `aiobungie.crates.profile.LinkedProfile`
 424            A linked profile object.
 425        """
 426        resp = await self.rest.fetch_linked_profiles(member_id, member_type, all=all)
 427
 428        return self.factory.deserialize_linked_profiles(resp)
 429
 430    async def fetch_player(
 431        self,
 432        name: str,
 433        code: int,
 434        /,
 435        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.ALL,
 436    ) -> collections.Sequence[user.DestinyMembership]:
 437        """Fetch a Destiny 2 player's memberships.
 438
 439        Parameters
 440        -----------
 441        name: `str`
 442            The unique Bungie player name.
 443        code : `int`
 444            The unique Bungie display name code.
 445        type: `aiobungie.internal.enums.MembershipType`
 446            The player's membership type, e,g. XBOX, STEAM, PSN
 447
 448        Returns
 449        --------
 450        `collections.Sequence[aiobungie.crates.DestinyMembership]`
 451            A sequence of the found Destiny 2 player memberships.
 452            An empty sequence will be returned if no one found.
 453
 454        Raises
 455        ------
 456        `aiobungie.MembershipTypeError`
 457            The provided membership type was invalid.
 458        """
 459        resp = await self.rest.fetch_player(name, code, type)
 460
 461        return self.factory.deserialize_destiny_memberships(resp)
 462
 463    async def fetch_character(
 464        self,
 465        member_id: int,
 466        membership_type: typedefs.IntAnd[enums.MembershipType],
 467        character_id: int,
 468        components: list[enums.ComponentType],
 469        auth: typing.Optional[str] = None,
 470    ) -> components.CharacterComponent:
 471        """Fetch a Destiny 2 character.
 472
 473        Parameters
 474        ----------
 475        member_id: `int`
 476            A valid bungie member id.
 477        character_id: `int`
 478            The Destiny character id to retrieve.
 479        membership_type: `aiobungie.internal.enums.MembershipType`
 480            The member's membership type.
 481        components: `list[aiobungie.ComponentType]`
 482            Multiple arguments of character components to collect and return.
 483
 484        Other Parameters
 485        ----------------
 486        auth : `typing.Optional[str]`
 487            A Bearer access_token to make the request with.
 488            This is optional and limited to components that only requires an Authorization token.
 489
 490        Returns
 491        -------
 492        `aiobungie.crates.CharacterComponent`
 493            A Bungie character component.
 494
 495        `aiobungie.MembershipTypeError`
 496            The provided membership type was invalid.
 497        """
 498        resp = await self.rest.fetch_character(
 499            member_id, membership_type, character_id, components, auth
 500        )
 501
 502        return self.factory.deserialize_character_component(resp)
 503
 504    async def fetch_unique_weapon_history(
 505        self,
 506        membership_id: int,
 507        character_id: int,
 508        membership_type: typedefs.IntAnd[enums.MembershipType],
 509    ) -> collections.Sequence[activity.ExtendedWeaponValues]:
 510        """Fetch details about unique weapon usage for a character. Includes all exotics.
 511
 512        Parameters
 513        ----------
 514        membership_id : `int`
 515            The Destiny user membership id.
 516        character_id : `int`
 517            The character id to retrieve.
 518        membership_type : `aiobungie.typedefs.IntAnd[aiobungie.MembershipType]`
 519            The Destiny user's membership type.
 520
 521        Returns
 522        -------
 523        `collections.Sequence[aiobungie.crates.ExtendedWeaponValues]`
 524            A sequence of the weapon's extended values.
 525        """
 526        resp = await self._rest.fetch_unique_weapon_history(
 527            membership_id, character_id, membership_type
 528        )
 529
 530        return [
 531            self._factory.deserialize_extended_weapon_values(weapon)
 532            for weapon in resp["weapons"]
 533        ]
 534
 535    # * Destiny 2 Activities.
 536
 537    async def fetch_activities(
 538        self,
 539        member_id: int,
 540        character_id: int,
 541        mode: typedefs.IntAnd[enums.GameMode],
 542        *,
 543        membership_type: typedefs.IntAnd[
 544            enums.MembershipType
 545        ] = enums.MembershipType.ALL,
 546        page: int = 0,
 547        limit: int = 250,
 548    ) -> iterators.Iterator[activity.Activity]:
 549        """Fetch a Destiny 2 activity for the specified character id.
 550
 551        Parameters
 552        ----------
 553        member_id: `int`
 554            The user id that starts with `4611`.
 555        character_id: `int`
 556            The id of the character to retrieve the activities for.
 557        mode: `aiobungie.typedefs.IntAnd[aiobungie.internal.enums.GameMode]`
 558            This parameter filters the game mode, Nightfall, Strike, Iron Banner, etc.
 559
 560        Other Parameters
 561        ----------------
 562        membership_type: `aiobungie.internal.enums.MembershipType`
 563            The Member ship type, if nothing was passed than it will return all.
 564        page: int
 565            The page number. Default is `0`
 566        limit: int
 567            Limit the returned result. Default is `250`.
 568
 569        Returns
 570        -------
 571        `aiobungie.iterators.Iterator[aiobungie.crates.Activity]`
 572            An iterator of the player's activities.
 573
 574        Raises
 575        ------
 576        `aiobungie.MembershipTypeError`
 577            The provided membership type was invalid.
 578        """
 579        resp = await self.rest.fetch_activities(
 580            member_id,
 581            character_id,
 582            mode,
 583            membership_type=membership_type,
 584            page=page,
 585            limit=limit,
 586        )
 587
 588        return self.factory.deserialize_activities(resp)
 589
 590    async def fetch_post_activity(self, instance_id: int, /) -> activity.PostActivity:
 591        """Fetch a post activity details.
 592
 593        Parameters
 594        ----------
 595        instance_id: `int`
 596            The activity instance id.
 597
 598        Returns
 599        -------
 600        `aiobungie.crates.PostActivity`
 601           A post activity object.
 602        """
 603        resp = await self.rest.fetch_post_activity(instance_id)
 604
 605        return self.factory.deserialize_post_activity(resp)
 606
 607    async def fetch_aggregated_activity_stats(
 608        self,
 609        character_id: int,
 610        membership_id: int,
 611        membership_type: typedefs.IntAnd[enums.MembershipType],
 612    ) -> iterators.Iterator[activity.AggregatedActivity]:
 613        """Fetch aggregated activity stats for a character.
 614
 615        Parameters
 616        ----------
 617        character_id: `int`
 618            The id of the character to retrieve the activities for.
 619        membership_id: `int`
 620            The id of the user that started with `4611`.
 621        membership_type: `aiobungie.internal.enums.MembershipType`
 622            The Member ship type.
 623
 624        Returns
 625        -------
 626        `aiobungie.iterators.Iterator[aiobungie.crates.AggregatedActivity]`
 627            An iterator of the player's activities.
 628
 629        Raises
 630        ------
 631        `aiobungie.MembershipTypeError`
 632            The provided membership type was invalid.
 633        """
 634        resp = await self.rest.fetch_aggregated_activity_stats(
 635            character_id, membership_id, membership_type
 636        )
 637
 638        return self.factory.deserialize_aggregated_activities(resp)
 639
 640    # * Destiny 2 Clans or GroupsV2.
 641
 642    async def fetch_clan_from_id(
 643        self,
 644        id: int,
 645        /,
 646        access_token: typing.Optional[str] = None,
 647    ) -> clans.Clan:
 648        """Fetch a Bungie Clan by its id.
 649
 650        Parameters
 651        -----------
 652        id: `int`
 653            The clan id.
 654
 655        Returns
 656        --------
 657        `aiobungie.crates.Clan`
 658            An Bungie clan.
 659
 660        Raises
 661        ------
 662        `aiobungie.NotFound`
 663            The clan was not found.
 664        """
 665        resp = await self.rest.fetch_clan_from_id(id, access_token)
 666
 667        return self.factory.deserialize_clan(resp)
 668
 669    async def fetch_clan(
 670        self,
 671        name: str,
 672        /,
 673        access_token: typing.Optional[str] = None,
 674        *,
 675        type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
 676    ) -> clans.Clan:
 677        """Fetch a Clan by its name.
 678        This method will return the first clan found with given name.
 679
 680        Parameters
 681        ----------
 682        name: `str`
 683            The clan name
 684
 685        Other Parameters
 686        ----------------
 687        access_token : `typing.Optional[str]`
 688            An optional access token to make the request with.
 689
 690            If the token was bound to a member of the clan,
 691            This field `aiobungie.crates.Clan.current_user_membership` will be available
 692            and will return the membership of the user who made this request.
 693        type : `aiobungie.GroupType`
 694            The group type, Default is aiobungie.GroupType.CLAN.
 695
 696        Returns
 697        -------
 698        `aiobungie.crates.Clan`
 699            A Bungie clan.
 700
 701        Raises
 702        ------
 703        `aiobungie.NotFound`
 704            The clan was not found.
 705        """
 706        resp = await self.rest.fetch_clan(name, access_token, type=type)
 707
 708        return self.factory.deserialize_clan(resp)
 709
 710    async def fetch_clan_conversations(
 711        self, clan_id: int, /
 712    ) -> collections.Sequence[clans.ClanConversation]:
 713        """Fetch the conversations/chat channels of the given clan id.
 714
 715        Parameters
 716        ----------
 717        clan_id : `int`
 718            The clan id.
 719
 720        Returns
 721        `collections.Sequence[aiobungie.crates.ClanConversation]`
 722            A sequence of the clan chat channels.
 723        """
 724        resp = await self.rest.fetch_clan_conversations(clan_id)
 725
 726        return self.factory.deserialize_clan_conversations(resp)
 727
 728    async def fetch_clan_admins(
 729        self, clan_id: int, /
 730    ) -> iterators.Iterator[clans.ClanMember]:
 731        """Fetch the clan founder and admins.
 732
 733        Parameters
 734        ----------
 735        clan_id : `int`
 736            The clan id.
 737
 738        Returns
 739        -------
 740        `aiobungie.iterators.Iterator[aiobungie.crates.ClanMember]`
 741            An iterator over the found clan admins and founder.
 742
 743        Raises
 744        ------
 745        `aiobungie.NotFound`
 746            The requested clan was not found.
 747        """
 748        resp = await self.rest.fetch_clan_admins(clan_id)
 749
 750        return self.factory.deserialize_clan_members(resp)
 751
 752    async def fetch_groups_for_member(
 753        self,
 754        member_id: int,
 755        member_type: typedefs.IntAnd[enums.MembershipType],
 756        /,
 757        *,
 758        filter: int = 0,
 759        group_type: enums.GroupType = enums.GroupType.CLAN,
 760    ) -> collections.Sequence[clans.GroupMember]:
 761        """Fetch information about the groups that a given member has joined.
 762
 763        Parameters
 764        ----------
 765        member_id : `int`
 766            The member's id
 767        member_type : `aiobungie.MembershipType`
 768            The member's membership type.
 769
 770        Other Parameters
 771        ----------------
 772        filter : `int`
 773            Filter apply to list of joined groups. This Default to `0`
 774        group_type : `aiobungie.GroupType`
 775            The group's type.
 776            This is always set to `aiobungie.GroupType.CLAN` and should not be changed.
 777
 778        Returns
 779        -------
 780        `collections.Sequence[aiobungie.crates.GroupMember]`
 781            A sequence of joined groups for the fetched member.
 782        """
 783        resp = await self.rest.fetch_groups_for_member(
 784            member_id, member_type, filter=filter, group_type=group_type
 785        )
 786
 787        return [
 788            self.factory.deserialize_group_member(group) for group in resp["results"]
 789        ]
 790
 791    async def fetch_potential_groups_for_member(
 792        self,
 793        member_id: int,
 794        member_type: typedefs.IntAnd[enums.MembershipType],
 795        /,
 796        *,
 797        filter: int = 0,
 798        group_type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
 799    ) -> collections.Sequence[clans.GroupMember]:
 800        """Fetch the potential groups for a clan member.
 801
 802        Parameters
 803        ----------
 804        member_id : `int`
 805            The member's id
 806        member_type : `aiobungie.typedefs.IntAnd[aiobungie.MembershipType]`
 807            The member's membership type.
 808
 809        Other Parameters
 810        ----------------
 811        filter : `int`
 812            Filter apply to list of joined groups. This Default to `0`
 813        group_type : `aiobungie.typedefs.IntAnd[aiobungie.GroupType]`
 814            The group's type.
 815            This is always set to `aiobungie.GroupType.CLAN` and should not be changed.
 816
 817        Returns
 818        -------
 819        `collections.Sequence[aiobungie.crates.GroupMember]`
 820            A sequence of joined potential groups for the fetched member.
 821        """
 822        resp = await self.rest.fetch_potential_groups_for_member(
 823            member_id, member_type, filter=filter, group_type=group_type
 824        )
 825
 826        return [
 827            self.factory.deserialize_group_member(group) for group in resp["results"]
 828        ]
 829
 830    async def fetch_clan_members(
 831        self,
 832        clan_id: int,
 833        /,
 834        *,
 835        name: typing.Optional[str] = None,
 836        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.NONE,
 837    ) -> iterators.Iterator[clans.ClanMember]:
 838        """Fetch Bungie clan members.
 839
 840        Parameters
 841        ----------
 842        clan_id : `int`
 843            The clans id
 844
 845        Other Parameters
 846        ----------------
 847        name : `typing.Optional[str]`
 848            If provided, Only players matching this name will be returned.
 849        type : `aiobungie.MembershipType`
 850            An optional clan member's membership type.
 851            This parameter is used to filter the returned results
 852            by the provided membership, For an example XBox memberships only,
 853            Otherwise will return all memberships.
 854
 855        Returns
 856        -------
 857        `aiobungie.iterators.Iterator[aiobungie.crates.ClanMember]`
 858            An iterator over the bungie clan members.
 859
 860        Raises
 861        ------
 862        `aiobungie.NotFound`
 863            The clan was not found.
 864        """
 865        resp = await self.rest.fetch_clan_members(clan_id, type=type, name=name)
 866
 867        return self.factory.deserialize_clan_members(resp)
 868
 869    async def fetch_clan_banners(self) -> collections.Sequence[clans.ClanBanner]:
 870        """Fetch the clan banners.
 871
 872        Returns
 873        -------
 874        `collections.Sequence[aiobungie.crates.ClanBanner]`
 875            A sequence of the clan banners.
 876        """
 877        resp = await self.rest.fetch_clan_banners()
 878
 879        return self.factory.deserialize_clan_banners(resp)
 880
 881    # This method is required to be here since it deserialize the clan.
 882    async def kick_clan_member(
 883        self,
 884        access_token: str,
 885        /,
 886        group_id: int,
 887        membership_id: int,
 888        membership_type: typedefs.IntAnd[enums.MembershipType],
 889    ) -> clans.Clan:
 890        """Kick a member from the clan.
 891
 892        .. note::
 893            This request requires OAuth2: oauth2: `AdminGroups` scope.
 894
 895        Parameters
 896        ----------
 897        access_token : `str`
 898            The bearer access token associated with the bungie account.
 899        group_id: `int`
 900            The group id.
 901        membership_id : `int`
 902            The member id to kick.
 903        membership_type : `aiobungie.typedefs.IntAnd[aiobungie.MembershipType]`
 904            The member's membership type.
 905
 906        Returns
 907        -------
 908        `aiobungie.crates.clan.Clan`
 909            The clan that the member was kicked from.
 910        """
 911        resp = await self.rest.kick_clan_member(
 912            access_token,
 913            group_id=group_id,
 914            membership_id=membership_id,
 915            membership_type=membership_type,
 916        )
 917
 918        return self.factory.deserialize_clan(resp)
 919
 920    async def fetch_clan_weekly_rewards(self, clan_id: int) -> milestones.Milestone:
 921        """Fetch a Bungie clan's weekly reward state.
 922
 923        Parameters
 924        ----------
 925        clan_id : `int`
 926            The clan's id.
 927
 928        Returns
 929        -------
 930        `aiobungie.crates.Milestone`
 931            A runtime status of the clan's milestone data.
 932        """
 933
 934        resp = await self.rest.fetch_clan_weekly_rewards(clan_id)
 935
 936        return self.factory.deserialize_milestone(resp)
 937
 938    # * Destiny 2 Entities aka Definitions.
 939
 940    async def fetch_inventory_item(self, hash: int, /) -> entity.InventoryEntity:
 941        """Fetch a static inventory item entity given a its hash.
 942
 943        Parameters
 944        ----------
 945        hash: `int`
 946            Inventory item's hash.
 947
 948        Returns
 949        -------
 950        `aiobungie.crates.InventoryEntity`
 951            A bungie inventory item.
 952        """
 953        resp = await self.rest.fetch_inventory_item(hash)
 954
 955        return self.factory.deserialize_inventory_entity(resp)
 956
 957    async def fetch_objective_entity(self, hash: int, /) -> entity.ObjectiveEntity:
 958        """Fetch a Destiny objective entity given a its hash.
 959
 960        Parameters
 961        ----------
 962        hash: `int`
 963            objective's hash.
 964
 965        Returns
 966        -------
 967        `aiobungie.crates.ObjectiveEntity`
 968            An objective entity item.
 969        """
 970        resp = await self.rest.fetch_objective_entity(hash)
 971
 972        return self.factory.deserialize_objective_entity(resp)
 973
 974    async def search_entities(
 975        self, name: str, entity_type: str, *, page: int = 0
 976    ) -> iterators.Iterator[entity.SearchableEntity]:
 977        """Search for Destiny2 entities given a name and its type.
 978
 979        Parameters
 980        ----------
 981        name : `str`
 982            The name of the entity, i.e., Thunderlord, One thousand voices.
 983        entity_type : `str`
 984            The type of the entity, AKA Definition,
 985            For an example `DestinyInventoryItemDefinition` for emblems, weapons, and other inventory items.
 986
 987        Other Parameters
 988        ----------------
 989        page : `int`
 990            An optional page to return. Default to 0.
 991
 992        Returns
 993        -------
 994        `aiobungie.iterators.Iterator[aiobungie.crates.SearchableEntity]`
 995            An iterator over the found results matching the provided name.
 996        """
 997        resp = await self.rest.search_entities(name, entity_type, page=page)
 998
 999        return self.factory.deserialize_inventory_results(resp)
1000
1001    # Fireteams
1002
1003    async def fetch_fireteams(
1004        self,
1005        activity_type: typedefs.IntAnd[fireteams.FireteamActivity],
1006        *,
1007        platform: typedefs.IntAnd[
1008            fireteams.FireteamPlatform
1009        ] = fireteams.FireteamPlatform.ANY,
1010        language: typing.Union[
1011            fireteams.FireteamLanguage, str
1012        ] = fireteams.FireteamLanguage.ALL,
1013        date_range: int = 0,
1014        page: int = 0,
1015        slots_filter: int = 0,
1016    ) -> typing.Optional[collections.Sequence[fireteams.Fireteam]]:
1017        """Fetch public Bungie fireteams with open slots.
1018
1019        Parameters
1020        ----------
1021        activity_type : `aiobungie.typedefs.IntAnd[aiobungie.crates.FireteamActivity]`
1022            The fireteam activity type.
1023
1024        Other Parameters
1025        ----------------
1026        platform : `aiobungie.typedefs.IntAnd[aiobungie.crates.fireteams.FireteamPlatform]`
1027            If this is provided. Then the results will be filtered with the given platform.
1028            Defaults to `aiobungie.crates.FireteamPlatform.ANY` which returns all platforms.
1029        language : `typing.Union[aiobungie.crates.fireteams.FireteamLanguage, str]`
1030            A locale language to filter the used language in that fireteam.
1031            Defaults to `aiobungie.crates.FireteamLanguage.ALL`
1032        date_range : `int`
1033            An integer to filter the date range of the returned fireteams. Defaults to `aiobungie.FireteamDate.ALL`.
1034        page : `int`
1035            The page number. By default its `0` which returns all available activities.
1036        slots_filter : `int`
1037            Filter the returned fireteams based on available slots. Default is `0`
1038
1039        Returns
1040        -------
1041        `typing.Optional[collections.Sequence[fireteams.Fireteam]]`
1042            A sequence of `aiobungie.crates.Fireteam` or `None`.
1043        """
1044
1045        resp = await self.rest.fetch_fireteams(
1046            activity_type,
1047            platform=platform,
1048            language=language,
1049            date_range=date_range,
1050            page=page,
1051            slots_filter=slots_filter,
1052        )
1053
1054        return self.factory.deserialize_fireteams(resp)
1055
1056    async def fetch_avaliable_clan_fireteams(
1057        self,
1058        access_token: str,
1059        group_id: int,
1060        activity_type: typedefs.IntAnd[fireteams.FireteamActivity],
1061        *,
1062        platform: typedefs.IntAnd[fireteams.FireteamPlatform],
1063        language: typing.Union[fireteams.FireteamLanguage, str],
1064        date_range: int = 0,
1065        page: int = 0,
1066        public_only: bool = False,
1067        slots_filter: int = 0,
1068    ) -> typing.Optional[collections.Sequence[fireteams.Fireteam]]:
1069        """Fetch a clan's fireteams with open slots.
1070
1071        .. note::
1072            This method requires OAuth2: ReadGroups scope.
1073
1074        Parameters
1075        ----------
1076        access_token : `str`
1077            The bearer access token associated with the bungie account.
1078        group_id : `int`
1079            The group/clan id of the fireteam.
1080        activity_type : `aiobungie.typedefs.IntAnd[aiobungie.crates.FireteamActivity]`
1081            The fireteam activity type.
1082
1083        Other Parameters
1084        ----------------
1085        platform : `aiobungie.typedefs.IntAnd[aiobungie.crates.fireteams.FireteamPlatform]`
1086            If this is provided. Then the results will be filtered with the given platform.
1087            Defaults to `aiobungie.crates.FireteamPlatform.ANY` which returns all platforms.
1088        language : `typing.Union[aiobungie.crates.fireteams.FireteamLanguage, str]`
1089            A locale language to filter the used language in that fireteam.
1090            Defaults to `aiobungie.crates.FireteamLanguage.ALL`
1091        date_range : `int`
1092            An integer to filter the date range of the returned fireteams. Defaults to `0`.
1093        page : `int`
1094            The page number. By default its `0` which returns all available activities.
1095        public_only: `bool`
1096            If set to True, Then only public fireteams will be returned.
1097        slots_filter : `int`
1098            Filter the returned fireteams based on available slots. Default is `0`
1099
1100        Returns
1101        -------
1102        `typing.Optional[collections.Sequence[aiobungie.crates.Fireteam]]`
1103            A sequence of  fireteams found in the clan.
1104            `None` will be returned if nothing was found.
1105        """
1106        resp = await self.rest.fetch_avaliable_clan_fireteams(
1107            access_token,
1108            group_id,
1109            activity_type,
1110            platform=platform,
1111            language=language,
1112            date_range=date_range,
1113            page=page,
1114            public_only=public_only,
1115            slots_filter=slots_filter,
1116        )
1117
1118        return self.factory.deserialize_fireteams(resp)
1119
1120    async def fetch_clan_fireteam(
1121        self, access_token: str, fireteam_id: int, group_id: int
1122    ) -> fireteams.AvailableFireteam:
1123        """Fetch a specific clan fireteam.
1124
1125        .. note::
1126            This method requires OAuth2: ReadGroups scope.
1127
1128        Parameters
1129        ----------
1130        access_token : `str`
1131            The bearer access token associated with the bungie account.
1132        group_id : `int`
1133            The group/clan id to fetch the fireteam from.
1134        fireteam_id : `int`
1135            The fireteam id to fetch.
1136
1137        Returns
1138        -------
1139        `typing.Optional[aiobungie.crates.AvailableFireteam]`
1140            A sequence of available fireteams objects if exists. else `None` will be returned.
1141        """
1142        resp = await self.rest.fetch_clan_fireteam(access_token, fireteam_id, group_id)
1143
1144        return self.factory.deserialize_available_fireteams(
1145            resp, no_results=True
1146        )  # type: ignore[return-value]
1147
1148    async def fetch_my_clan_fireteams(
1149        self,
1150        access_token: str,
1151        group_id: int,
1152        *,
1153        include_closed: bool = True,
1154        platform: typedefs.IntAnd[fireteams.FireteamPlatform],
1155        language: typing.Union[fireteams.FireteamLanguage, str],
1156        filtered: bool = True,
1157        page: int = 0,
1158    ) -> collections.Sequence[fireteams.AvailableFireteam]:
1159        """A method that's similar to `fetch_fireteams` but requires OAuth2.
1160
1161        .. note::
1162            This method requires OAuth2: ReadGroups scope.
1163
1164        Parameters
1165        ----------
1166        access_token : str
1167            The bearer access token associated with the bungie account.
1168        group_id : int
1169            The group/clan id to fetch.
1170
1171        Other Parameters
1172        ----------------
1173        include_closed : bool
1174            If provided and set to True, It will also return closed fireteams.
1175            If provided and set to False, It will only return public fireteams. Default is True.
1176        platform : aiobungie.typedefs.IntAnd[aiobungie.crates.fireteams.FireteamPlatform]
1177            If this is provided. Then the results will be filtered with the given platform.
1178            Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
1179        language : typing.Union[aiobungie.crates.fireteams.FireteamLanguage, str]
1180            A locale language to filter the used language in that fireteam.
1181            Defaults to aiobungie.crates.FireteamLanguage.ALL
1182        filtered : bool
1183            If set to True, it will filter by clan. Otherwise not. Default is True.
1184        page : int
1185            The page number. By default its 0 which returns all available activities.
1186
1187        Returns
1188        -------
1189        `collections.Sequence[aiobungie.crates.AvailableFireteam]`
1190            A sequence of available fireteams objects if exists. else `None` will be returned.
1191        """
1192        resp = await self.rest.fetch_my_clan_fireteams(
1193            access_token,
1194            group_id,
1195            include_closed=include_closed,
1196            platform=platform,
1197            language=language,
1198            filtered=filtered,
1199            page=page,
1200        )
1201
1202        return self.factory.deserialize_available_fireteams(resp)  # type: ignore[return-value]
1203
1204    # Friends and social.
1205
1206    async def fetch_friends(
1207        self, access_token: str, /
1208    ) -> collections.Sequence[friends.Friend]:
1209        """Fetch bungie friend list.
1210
1211        .. note::
1212            This requests OAuth2: ReadUserData scope.
1213
1214        Parameters
1215        -----------
1216        access_token : `str`
1217            The bearer access token associated with the bungie account.
1218
1219        Returns
1220        -------
1221        `collections.Sequence[aiobungie.crates.Friend]`
1222            A sequence of the friends associated with that access token.
1223        """
1224
1225        resp = await self.rest.fetch_friends(access_token)
1226
1227        return self.factory.deserialize_friends(resp)
1228
1229    async def fetch_friend_requests(
1230        self, access_token: str, /
1231    ) -> friends.FriendRequestView:
1232        """Fetch pending bungie friend requests queue.
1233
1234        .. note::
1235            This requests OAuth2: ReadUserData scope.
1236
1237        Parameters
1238        -----------
1239        access_token : `str`
1240            The bearer access token associated with the bungie account.
1241
1242        Returns
1243        -------
1244        `aiobungie.crates.FriendRequestView`
1245            A friend requests view of that associated access token.
1246        """
1247
1248        resp = await self.rest.fetch_friend_requests(access_token)
1249
1250        return self.factory.deserialize_friend_requests(resp)
1251
1252    # Applications and Developer portal.
1253
1254    async def fetch_application(self, appid: int, /) -> application.Application:
1255        """Fetch a Bungie application.
1256
1257        Parameters
1258        -----------
1259        appid: `int`
1260            The application id.
1261
1262        Returns
1263        --------
1264        `aiobungie.crates.Application`
1265            A Bungie application.
1266        """
1267        resp = await self.rest.fetch_application(appid)
1268
1269        return self.factory.deserialize_app(resp)
1270
1271    # Milestones
1272
1273    async def fetch_public_milestone_content(
1274        self, milestone_hash: int, /
1275    ) -> milestones.MilestoneContent:
1276        """Fetch the milestone content given its hash.
1277
1278        Parameters
1279        ----------
1280        milestone_hash : `int`
1281            The milestone hash.
1282
1283        Returns
1284        -------
1285        `aiobungie.crates.milestones.MilestoneContent`
1286            A milestone content object.
1287        """
1288        resp = await self.rest.fetch_public_milestone_content(milestone_hash)
1289
1290        return self.factory.deserialize_public_milestone_content(resp)

Standard Bungie API client application.

This client deserialize the REST JSON responses using aiobungie.Factory and returns aiobungie.crates Python object implementations of the responses.

A aiobungie.RESTClient REST client can also be used alone for low-level concepts.

Example
import aiobungie

client = aiobungie.Client('...')

async def main():
    async with client.rest:
        user = await client.fetch_current_user_memberships('...')
        print(user)
Parameters
  • token (str): Your Bungie's API key or Token from the developer's portal.
Other Parameters
  • rest_client (aiobungie.interfaces.RESTInterface | None): An optional rest client instance you can pass. If set to None then the client will use the default instance.
  • max_retries (int): The max retries number to retry if the request hit a 5xx status code.
  • max_ratelimit_retries (int): The max retries number to retry if the request hit a 429 status code. Defaults to 3.
  • client_secret (str | None): An optional application client secret, This is only needed if you're fetching OAuth2 tokens with this client.
  • client_id (int | None): An optional application client id, This is only needed if you're fetching OAuth2 tokens with this client.
Client( token: str, /, client_secret: Optional[str] = None, client_id: Optional[int] = None, *, rest_client: Optional[aiobungie.interfaces.rest.RESTInterface] = None, max_retries: int = 4, max_ratelimit_retries: int = 3)
107    def __init__(
108        self,
109        token: str,
110        /,
111        client_secret: typing.Optional[str] = None,
112        client_id: typing.Optional[int] = None,
113        *,
114        rest_client: typing.Optional[interfaces.RESTInterface] = None,
115        max_retries: int = 4,
116        max_ratelimit_retries: int = 3,
117    ) -> None:
118
119        self._client_secret = client_secret
120        self._client_id = client_id
121
122        self._rest = (
123            rest_client
124            if rest_client is not None
125            else rest_.RESTClient(
126                token,
127                client_secret,
128                client_id,
129                max_retries=max_retries,
130                max_ratelimit_retries=max_ratelimit_retries,
131            )
132        )
133
134        self._factory = factory_.Factory(self)

Returns the marshalling factory for the client.

rest: aiobungie.interfaces.rest.RESTInterface

Returns the REST client for the this client.

request: aiobungie.Client

A readonly ClientApp instance used for external requests.

metadata: collections.abc.MutableMapping[typing.Any, typing.Any]

A mutable mapping storage for the user's needs.

def run( self, future: collections.abc.Coroutine[typing.Any, None, None], debug: bool = False) -> None:
152    def run(
153        self, future: collections.Coroutine[typing.Any, None, None], debug: bool = False
154    ) -> None:
155        loop: typing.Final[asyncio.AbstractEventLoop] = helpers.get_or_make_loop()
156        try:
157            if not loop.is_running():
158                loop.set_debug(debug)
159                loop.run_until_complete(future)
160
161        except Exception as exc:
162            raise RuntimeError(f"Failed to run {future.__qualname__}") from exc
163
164        except KeyboardInterrupt:
165            _LOG.warn("Unexpected Keyboard interrupt. Exiting.")
166            return

Runs a coroutine function until its complete.

This is equivalent to asyncio.get_event_loop().run_until_complete(...)

Parameters
  • future (collections.Coroutine[None, None, None]): A coroutine object.
  • debug (bool): Either to enable asyncio debug or not. Disabled by default.
Example
async def main() -> None:
    await fetch(...)

# Run the coroutine.
client.run(main())
async def fetch_current_user_memberships(self, access_token: str, /) -> aiobungie.crates.user.User:
170    async def fetch_current_user_memberships(self, access_token: str, /) -> user.User:
171        """Fetch and return a user object of the bungie net user associated with account.
172
173        .. warning::
174            This method requires OAuth2 scope and a Bearer access token.
175
176        Parameters
177        ----------
178        access_token : `str`
179            A valid Bearer access token for the authorization.
180
181        Returns
182        -------
183        `aiobungie.crates.user.User`
184            A user object includes the Destiny memberships and Bungie.net user.
185        """
186        resp = await self.rest.fetch_current_user_memberships(access_token)
187
188        return self.factory.deserialize_user(resp)

Fetch and return a user object of the bungie net user associated with account.

This method requires OAuth2 scope and a Bearer access token.

Parameters
  • access_token (str): A valid Bearer access token for the authorization.
Returns
  • aiobungie.crates.user.User: A user object includes the Destiny memberships and Bungie.net user.
async def fetch_bungie_user(self, id: int, /) -> aiobungie.crates.user.BungieUser:
190    async def fetch_bungie_user(self, id: int, /) -> user.BungieUser:
191        """Fetch a Bungie user by their BungieNet id.
192
193        .. note::
194            This returns a Bungie user membership only. Take a look at `Client.fetch_membership_from_id`
195            for other memberships.
196
197        Parameters
198        ----------
199        id: `int`
200            The user id.
201
202        Returns
203        -------
204        `aiobungie.crates.user.BungieUser`
205            A Bungie user.
206
207        Raises
208        ------
209        `aiobungie.error.NotFound`
210            The user was not found.
211        """
212        payload = await self.rest.fetch_bungie_user(id)
213
214        return self.factory.deserialize_bungie_user(payload)

Fetch a Bungie user by their BungieNet id.

This returns a Bungie user membership only. Take a look at Client.fetch_membership_from_id for other memberships.

Parameters
  • id (int): The user id.
Returns
  • aiobungie.crates.user.BungieUser: A Bungie user.
Raises
async def search_users( self, name: str, /) -> aiobungie.Iterator[aiobungie.crates.user.SearchableDestinyUser]:
216    async def search_users(
217        self, name: str, /
218    ) -> iterators.Iterator[user.SearchableDestinyUser]:
219        """Search for players and return all players that matches the same name.
220
221        Parameters
222        ----------
223        name : `buildins.str`
224            The user name.
225
226        Returns
227        -------
228        `aiobungie.iterators.Iterator[aiobungie.crates.DestinyMembership]`
229            A sequence of destiny memberships.
230        """
231        payload = await self.rest.search_users(name)
232
233        return iterators.Iterator(
234            [
235                self.factory.deserialize_searched_user(user)
236                for user in payload["searchResults"]
237            ]
238        )

Search for players and return all players that matches the same name.

Parameters
  • name (buildins.str): The user name.
Returns
async def fetch_user_themes(self) -> collections.abc.Sequence[aiobungie.crates.user.UserThemes]:
240    async def fetch_user_themes(self) -> collections.Sequence[user.UserThemes]:
241        """Fetch all available user themes.
242
243        Returns
244        -------
245        `collections.Sequence[aiobungie.crates.user.UserThemes]`
246            A sequence of user themes.
247        """
248        data = await self.rest.fetch_user_themes()
249
250        return self.factory.deserialize_user_themes(data)

Fetch all available user themes.

Returns
  • collections.Sequence[aiobungie.crates.user.UserThemes]: A sequence of user themes.
async def fetch_hard_types( self, credential: int, type: Union[int, aiobungie.CredentialType] = <CredentialType.STEAMID: 12>, /) -> aiobungie.crates.user.HardLinkedMembership:
252    async def fetch_hard_types(
253        self,
254        credential: int,
255        type: typedefs.IntAnd[enums.CredentialType] = enums.CredentialType.STEAMID,
256        /,
257    ) -> user.HardLinkedMembership:
258        """Gets any hard linked membership given a credential.
259        Only works for credentials that are public just `aiobungie.CredentialType.STEAMID` right now.
260        Cross Save aware.
261
262        Parameters
263        ----------
264        credential: `int`
265            A valid SteamID64
266        type: `aiobungie.CredentialType`
267            The credential type. This must not be changed
268            Since its only credential that works "currently"
269
270        Returns
271        -------
272        `aiobungie.crates.user.HardLinkedMembership`
273            Information about the hard linked data.
274        """
275
276        payload = await self.rest.fetch_hardlinked_credentials(credential, type)
277
278        return user.HardLinkedMembership(
279            id=int(payload["membershipId"]),
280            type=enums.MembershipType(payload["membershipType"]),
281            cross_save_type=enums.MembershipType(payload["CrossSaveOverriddenType"]),
282        )

Gets any hard linked membership given a credential. Only works for credentials that are public just aiobungie.CredentialType.STEAMID right now. Cross Save aware.

Parameters
  • credential (int): A valid SteamID64
  • type (aiobungie.CredentialType): The credential type. This must not be changed Since its only credential that works "currently"
Returns
  • aiobungie.crates.user.HardLinkedMembership: Information about the hard linked data.
async def fetch_membership_from_id( self, id: int, /, type: Union[int, aiobungie.MembershipType] = <MembershipType.NONE: 0>) -> aiobungie.crates.user.User:
284    async def fetch_membership_from_id(
285        self,
286        id: int,
287        /,
288        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.NONE,
289    ) -> user.User:
290        """Fetch Bungie user's memberships from their id.
291
292        Notes
293        -----
294        * This returns both BungieNet membership and a sequence of the player's DestinyMemberships
295        Which includes Stadia, Xbox, Steam and PSN memberships if the player has them,
296        see `aiobungie.crates.user.DestinyMembership` for more details.
297        * If you only want the bungie user. Consider using `Client.fetch_user` method.
298
299        Parameters
300        ----------
301        id : `int`
302            The user's id.
303        type : `aiobungie.MembershipType`
304            The user's membership type.
305
306        Returns
307        -------
308        `aiobungie.crates.User`
309            A Bungie user with their membership types.
310
311        Raises
312        ------
313        aiobungie.NotFound
314            The requested user was not found.
315        """
316        payload = await self.rest.fetch_membership_from_id(id, type)
317
318        return self.factory.deserialize_user(payload)

Fetch Bungie user's memberships from their id.

Notes
  • This returns both BungieNet membership and a sequence of the player's DestinyMemberships Which includes Stadia, Xbox, Steam and PSN memberships if the player has them, see aiobungie.crates.user.DestinyMembership for more details.
  • If you only want the bungie user. Consider using Client.fetch_user method.
Parameters
Returns
Raises
async def fetch_user_credentials( self, access_token: str, membership_id: int, /) -> collections.abc.Sequence[aiobungie.crates.user.UserCredentials]:
320    async def fetch_user_credentials(
321        self, access_token: str, membership_id: int, /
322    ) -> collections.Sequence[user.UserCredentials]:
323        """Fetch an array of credential types attached to the requested account.
324
325        .. note::
326            This method require OAuth2 Bearer access token.
327
328        Parameters
329        ----------
330        access_token : `str`
331            The bearer access token associated with the bungie account.
332        membership_id : `int`
333            The id of the membership to return.
334
335        Returns
336        -------
337        `collections.Sequence[aiobungie.crates.UserCredentials]`
338            A sequence of the attached user credentials.
339
340        Raises
341        ------
342        `aiobungie.Unauthorized`
343            The access token was wrong or no access token passed.
344        """
345        resp = await self.rest.fetch_user_credentials(access_token, membership_id)
346
347        return self.factory.deserialize_user_credentials(resp)

Fetch an array of credential types attached to the requested account.

This method require OAuth2 Bearer access token.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • membership_id (int): The id of the membership to return.
Returns
Raises
async def fetch_profile( self, member_id: int, type: Union[int, aiobungie.MembershipType], components: list[aiobungie.ComponentType], auth: Optional[str] = None) -> aiobungie.crates.components.Component:
351    async def fetch_profile(
352        self,
353        member_id: int,
354        type: typedefs.IntAnd[enums.MembershipType],
355        components: list[enums.ComponentType],
356        auth: typing.Optional[str] = None,
357    ) -> components.Component:
358        """
359        Fetch a bungie profile passing components to the request.
360
361        Parameters
362        ----------
363        member_id: `int`
364            The member's id.
365        type: `aiobungie.MembershipType`
366            A valid membership type.
367        components : `list[aiobungie.ComponentType]`
368            List of profile components to collect and return.
369
370        Other Parameters
371        ----------------
372        auth : `typing.Optional[str]`
373            A Bearer access_token to make the request with.
374            This is optional and limited to components that only requires an Authorization token.
375
376        Returns
377        --------
378        `aiobungie.crates.Component`
379            A Destiny 2 player profile with its components.
380            Only passed components will be available if they exists. Otherwise they will be `None`
381
382        Raises
383        ------
384        `aiobungie.MembershipTypeError`
385            The provided membership type was invalid.
386        """
387        data = await self.rest.fetch_profile(member_id, type, components, auth)
388        return self.factory.deserialize_components(data)

Fetch a bungie profile passing components to the request.

Parameters
Other Parameters
  • auth (typing.Optional[str]): A Bearer access_token to make the request with. This is optional and limited to components that only requires an Authorization token.
Returns
  • aiobungie.crates.Component: A Destiny 2 player profile with its components. Only passed components will be available if they exists. Otherwise they will be None
Raises
async def fetch_linked_profiles( self, member_id: int, member_type: Union[int, aiobungie.MembershipType], /, *, all: bool = False) -> aiobungie.crates.profile.LinkedProfile:
390    async def fetch_linked_profiles(
391        self,
392        member_id: int,
393        member_type: typedefs.IntAnd[enums.MembershipType],
394        /,
395        *,
396        all: bool = False,
397    ) -> profile.LinkedProfile:
398        """Returns a summary information about all profiles linked to the requested member.
399
400        The passed membership id/type maybe a Bungie.Net membership or a Destiny memberships.
401
402        .. note::
403            It will only return linked accounts whose linkages you are allowed to view.
404
405        Parameters
406        ----------
407        member_id : `int`
408            The ID of the membership. This must be a valid Bungie.Net or PSN or Xbox ID.
409        member_type : `aiobungie.MembershipType`
410            The type for the membership whose linked Destiny account you want to return.
411
412        Other Parameters
413        ----------------
414        all : `bool`
415            If provided and set to `True`, All memberships regardless
416            of whether they're obscured by overrides will be returned,
417
418            If provided and set to `False`, Only available memberships will be returned.
419            The default for this is `False`.
420
421        Returns
422        -------
423        `aiobungie.crates.profile.LinkedProfile`
424            A linked profile object.
425        """
426        resp = await self.rest.fetch_linked_profiles(member_id, member_type, all=all)
427
428        return self.factory.deserialize_linked_profiles(resp)

Returns a summary information about all profiles linked to the requested member.

The passed membership id/type maybe a Bungie.Net membership or a Destiny memberships.

It will only return linked accounts whose linkages you are allowed to view.

Parameters
  • member_id (int): The ID of the membership. This must be a valid Bungie.Net or PSN or Xbox ID.
  • member_type (aiobungie.MembershipType): The type for the membership whose linked Destiny account you want to return.
Other Parameters
  • all (bool): If provided and set to True, All memberships regardless of whether they're obscured by overrides will be returned,

    If provided and set to False, Only available memberships will be returned. The default for this is False.

Returns
  • aiobungie.crates.profile.LinkedProfile: A linked profile object.
async def fetch_player( self, name: str, code: int, /, type: Union[int, aiobungie.MembershipType] = <MembershipType.ALL: -1>) -> collections.abc.Sequence[aiobungie.crates.user.DestinyMembership]:
430    async def fetch_player(
431        self,
432        name: str,
433        code: int,
434        /,
435        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.ALL,
436    ) -> collections.Sequence[user.DestinyMembership]:
437        """Fetch a Destiny 2 player's memberships.
438
439        Parameters
440        -----------
441        name: `str`
442            The unique Bungie player name.
443        code : `int`
444            The unique Bungie display name code.
445        type: `aiobungie.internal.enums.MembershipType`
446            The player's membership type, e,g. XBOX, STEAM, PSN
447
448        Returns
449        --------
450        `collections.Sequence[aiobungie.crates.DestinyMembership]`
451            A sequence of the found Destiny 2 player memberships.
452            An empty sequence will be returned if no one found.
453
454        Raises
455        ------
456        `aiobungie.MembershipTypeError`
457            The provided membership type was invalid.
458        """
459        resp = await self.rest.fetch_player(name, code, type)
460
461        return self.factory.deserialize_destiny_memberships(resp)

Fetch a Destiny 2 player's memberships.

Parameters
  • name (str): The unique Bungie player name.
  • code (int): The unique Bungie display name code.
  • type (aiobungie.MembershipType): The player's membership type, e,g. XBOX, STEAM, PSN
Returns
Raises
async def fetch_character( self, member_id: int, membership_type: Union[int, aiobungie.MembershipType], character_id: int, components: list[aiobungie.ComponentType], auth: Optional[str] = None) -> aiobungie.crates.components.CharacterComponent:
463    async def fetch_character(
464        self,
465        member_id: int,
466        membership_type: typedefs.IntAnd[enums.MembershipType],
467        character_id: int,
468        components: list[enums.ComponentType],
469        auth: typing.Optional[str] = None,
470    ) -> components.CharacterComponent:
471        """Fetch a Destiny 2 character.
472
473        Parameters
474        ----------
475        member_id: `int`
476            A valid bungie member id.
477        character_id: `int`
478            The Destiny character id to retrieve.
479        membership_type: `aiobungie.internal.enums.MembershipType`
480            The member's membership type.
481        components: `list[aiobungie.ComponentType]`
482            Multiple arguments of character components to collect and return.
483
484        Other Parameters
485        ----------------
486        auth : `typing.Optional[str]`
487            A Bearer access_token to make the request with.
488            This is optional and limited to components that only requires an Authorization token.
489
490        Returns
491        -------
492        `aiobungie.crates.CharacterComponent`
493            A Bungie character component.
494
495        `aiobungie.MembershipTypeError`
496            The provided membership type was invalid.
497        """
498        resp = await self.rest.fetch_character(
499            member_id, membership_type, character_id, components, auth
500        )
501
502        return self.factory.deserialize_character_component(resp)

Fetch a Destiny 2 character.

Parameters
  • member_id (int): A valid bungie member id.
  • character_id (int): The Destiny character id to retrieve.
  • membership_type (aiobungie.MembershipType): The member's membership type.
  • components (list[aiobungie.ComponentType]): Multiple arguments of character components to collect and return.
Other Parameters
  • auth (typing.Optional[str]): A Bearer access_token to make the request with. This is optional and limited to components that only requires an Authorization token.
Returns
async def fetch_unique_weapon_history( self, membership_id: int, character_id: int, membership_type: Union[int, aiobungie.MembershipType]) -> collections.abc.Sequence[aiobungie.crates.activity.ExtendedWeaponValues]:
504    async def fetch_unique_weapon_history(
505        self,
506        membership_id: int,
507        character_id: int,
508        membership_type: typedefs.IntAnd[enums.MembershipType],
509    ) -> collections.Sequence[activity.ExtendedWeaponValues]:
510        """Fetch details about unique weapon usage for a character. Includes all exotics.
511
512        Parameters
513        ----------
514        membership_id : `int`
515            The Destiny user membership id.
516        character_id : `int`
517            The character id to retrieve.
518        membership_type : `aiobungie.typedefs.IntAnd[aiobungie.MembershipType]`
519            The Destiny user's membership type.
520
521        Returns
522        -------
523        `collections.Sequence[aiobungie.crates.ExtendedWeaponValues]`
524            A sequence of the weapon's extended values.
525        """
526        resp = await self._rest.fetch_unique_weapon_history(
527            membership_id, character_id, membership_type
528        )
529
530        return [
531            self._factory.deserialize_extended_weapon_values(weapon)
532            for weapon in resp["weapons"]
533        ]

Fetch details about unique weapon usage for a character. Includes all exotics.

Parameters
Returns
async def fetch_activities( self, member_id: int, character_id: int, mode: Union[int, aiobungie.GameMode], *, membership_type: Union[int, aiobungie.MembershipType] = <MembershipType.ALL: -1>, page: int = 0, limit: int = 250) -> aiobungie.Iterator[aiobungie.crates.activity.Activity]:
537    async def fetch_activities(
538        self,
539        member_id: int,
540        character_id: int,
541        mode: typedefs.IntAnd[enums.GameMode],
542        *,
543        membership_type: typedefs.IntAnd[
544            enums.MembershipType
545        ] = enums.MembershipType.ALL,
546        page: int = 0,
547        limit: int = 250,
548    ) -> iterators.Iterator[activity.Activity]:
549        """Fetch a Destiny 2 activity for the specified character id.
550
551        Parameters
552        ----------
553        member_id: `int`
554            The user id that starts with `4611`.
555        character_id: `int`
556            The id of the character to retrieve the activities for.
557        mode: `aiobungie.typedefs.IntAnd[aiobungie.internal.enums.GameMode]`
558            This parameter filters the game mode, Nightfall, Strike, Iron Banner, etc.
559
560        Other Parameters
561        ----------------
562        membership_type: `aiobungie.internal.enums.MembershipType`
563            The Member ship type, if nothing was passed than it will return all.
564        page: int
565            The page number. Default is `0`
566        limit: int
567            Limit the returned result. Default is `250`.
568
569        Returns
570        -------
571        `aiobungie.iterators.Iterator[aiobungie.crates.Activity]`
572            An iterator of the player's activities.
573
574        Raises
575        ------
576        `aiobungie.MembershipTypeError`
577            The provided membership type was invalid.
578        """
579        resp = await self.rest.fetch_activities(
580            member_id,
581            character_id,
582            mode,
583            membership_type=membership_type,
584            page=page,
585            limit=limit,
586        )
587
588        return self.factory.deserialize_activities(resp)

Fetch a Destiny 2 activity for the specified character id.

Parameters
  • member_id (int): The user id that starts with 4611.
  • character_id (int): The id of the character to retrieve the activities for.
  • mode (aiobungie.typedefs.IntAnd[aiobungie.GameMode]): This parameter filters the game mode, Nightfall, Strike, Iron Banner, etc.
Other Parameters
  • membership_type (aiobungie.MembershipType): The Member ship type, if nothing was passed than it will return all.
  • page (int): The page number. Default is 0
  • limit (int): Limit the returned result. Default is 250.
Returns
Raises
async def fetch_post_activity(self, instance_id: int, /) -> aiobungie.crates.activity.PostActivity:
590    async def fetch_post_activity(self, instance_id: int, /) -> activity.PostActivity:
591        """Fetch a post activity details.
592
593        Parameters
594        ----------
595        instance_id: `int`
596            The activity instance id.
597
598        Returns
599        -------
600        `aiobungie.crates.PostActivity`
601           A post activity object.
602        """
603        resp = await self.rest.fetch_post_activity(instance_id)
604
605        return self.factory.deserialize_post_activity(resp)

Fetch a post activity details.

Parameters
  • instance_id (int): The activity instance id.
Returns
async def fetch_aggregated_activity_stats( self, character_id: int, membership_id: int, membership_type: Union[int, aiobungie.MembershipType]) -> aiobungie.Iterator[aiobungie.crates.activity.AggregatedActivity]:
607    async def fetch_aggregated_activity_stats(
608        self,
609        character_id: int,
610        membership_id: int,
611        membership_type: typedefs.IntAnd[enums.MembershipType],
612    ) -> iterators.Iterator[activity.AggregatedActivity]:
613        """Fetch aggregated activity stats for a character.
614
615        Parameters
616        ----------
617        character_id: `int`
618            The id of the character to retrieve the activities for.
619        membership_id: `int`
620            The id of the user that started with `4611`.
621        membership_type: `aiobungie.internal.enums.MembershipType`
622            The Member ship type.
623
624        Returns
625        -------
626        `aiobungie.iterators.Iterator[aiobungie.crates.AggregatedActivity]`
627            An iterator of the player's activities.
628
629        Raises
630        ------
631        `aiobungie.MembershipTypeError`
632            The provided membership type was invalid.
633        """
634        resp = await self.rest.fetch_aggregated_activity_stats(
635            character_id, membership_id, membership_type
636        )
637
638        return self.factory.deserialize_aggregated_activities(resp)

Fetch aggregated activity stats for a character.

Parameters
  • character_id (int): The id of the character to retrieve the activities for.
  • membership_id (int): The id of the user that started with 4611.
  • membership_type (aiobungie.MembershipType): The Member ship type.
Returns
Raises
async def fetch_clan_from_id( self, id: int, /, access_token: Optional[str] = None) -> aiobungie.crates.clans.Clan:
642    async def fetch_clan_from_id(
643        self,
644        id: int,
645        /,
646        access_token: typing.Optional[str] = None,
647    ) -> clans.Clan:
648        """Fetch a Bungie Clan by its id.
649
650        Parameters
651        -----------
652        id: `int`
653            The clan id.
654
655        Returns
656        --------
657        `aiobungie.crates.Clan`
658            An Bungie clan.
659
660        Raises
661        ------
662        `aiobungie.NotFound`
663            The clan was not found.
664        """
665        resp = await self.rest.fetch_clan_from_id(id, access_token)
666
667        return self.factory.deserialize_clan(resp)

Fetch a Bungie Clan by its id.

Parameters
  • id (int): The clan id.
Returns
Raises
async def fetch_clan( self, name: str, /, access_token: Optional[str] = None, *, type: Union[int, aiobungie.GroupType] = <GroupType.CLAN: 1>) -> aiobungie.crates.clans.Clan:
669    async def fetch_clan(
670        self,
671        name: str,
672        /,
673        access_token: typing.Optional[str] = None,
674        *,
675        type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
676    ) -> clans.Clan:
677        """Fetch a Clan by its name.
678        This method will return the first clan found with given name.
679
680        Parameters
681        ----------
682        name: `str`
683            The clan name
684
685        Other Parameters
686        ----------------
687        access_token : `typing.Optional[str]`
688            An optional access token to make the request with.
689
690            If the token was bound to a member of the clan,
691            This field `aiobungie.crates.Clan.current_user_membership` will be available
692            and will return the membership of the user who made this request.
693        type : `aiobungie.GroupType`
694            The group type, Default is aiobungie.GroupType.CLAN.
695
696        Returns
697        -------
698        `aiobungie.crates.Clan`
699            A Bungie clan.
700
701        Raises
702        ------
703        `aiobungie.NotFound`
704            The clan was not found.
705        """
706        resp = await self.rest.fetch_clan(name, access_token, type=type)
707
708        return self.factory.deserialize_clan(resp)

Fetch a Clan by its name. This method will return the first clan found with given name.

Parameters
  • name (str): The clan name
Other Parameters
Returns
Raises
async def fetch_clan_conversations( self, clan_id: int, /) -> collections.abc.Sequence[aiobungie.crates.clans.ClanConversation]:
710    async def fetch_clan_conversations(
711        self, clan_id: int, /
712    ) -> collections.Sequence[clans.ClanConversation]:
713        """Fetch the conversations/chat channels of the given clan id.
714
715        Parameters
716        ----------
717        clan_id : `int`
718            The clan id.
719
720        Returns
721        `collections.Sequence[aiobungie.crates.ClanConversation]`
722            A sequence of the clan chat channels.
723        """
724        resp = await self.rest.fetch_clan_conversations(clan_id)
725
726        return self.factory.deserialize_clan_conversations(resp)

Fetch the conversations/chat channels of the given clan id.

Parameters
async def fetch_clan_admins( self, clan_id: int, /) -> aiobungie.Iterator[aiobungie.crates.clans.ClanMember]:
728    async def fetch_clan_admins(
729        self, clan_id: int, /
730    ) -> iterators.Iterator[clans.ClanMember]:
731        """Fetch the clan founder and admins.
732
733        Parameters
734        ----------
735        clan_id : `int`
736            The clan id.
737
738        Returns
739        -------
740        `aiobungie.iterators.Iterator[aiobungie.crates.ClanMember]`
741            An iterator over the found clan admins and founder.
742
743        Raises
744        ------
745        `aiobungie.NotFound`
746            The requested clan was not found.
747        """
748        resp = await self.rest.fetch_clan_admins(clan_id)
749
750        return self.factory.deserialize_clan_members(resp)

Fetch the clan founder and admins.

Parameters
  • clan_id (int): The clan id.
Returns
Raises
async def fetch_groups_for_member( self, member_id: int, member_type: Union[int, aiobungie.MembershipType], /, *, filter: int = 0, group_type: aiobungie.GroupType = <GroupType.CLAN: 1>) -> collections.abc.Sequence[aiobungie.crates.clans.GroupMember]:
752    async def fetch_groups_for_member(
753        self,
754        member_id: int,
755        member_type: typedefs.IntAnd[enums.MembershipType],
756        /,
757        *,
758        filter: int = 0,
759        group_type: enums.GroupType = enums.GroupType.CLAN,
760    ) -> collections.Sequence[clans.GroupMember]:
761        """Fetch information about the groups that a given member has joined.
762
763        Parameters
764        ----------
765        member_id : `int`
766            The member's id
767        member_type : `aiobungie.MembershipType`
768            The member's membership type.
769
770        Other Parameters
771        ----------------
772        filter : `int`
773            Filter apply to list of joined groups. This Default to `0`
774        group_type : `aiobungie.GroupType`
775            The group's type.
776            This is always set to `aiobungie.GroupType.CLAN` and should not be changed.
777
778        Returns
779        -------
780        `collections.Sequence[aiobungie.crates.GroupMember]`
781            A sequence of joined groups for the fetched member.
782        """
783        resp = await self.rest.fetch_groups_for_member(
784            member_id, member_type, filter=filter, group_type=group_type
785        )
786
787        return [
788            self.factory.deserialize_group_member(group) for group in resp["results"]
789        ]

Fetch information about the groups that a given member has joined.

Parameters
Other Parameters
Returns
async def fetch_potential_groups_for_member( self, member_id: int, member_type: Union[int, aiobungie.MembershipType], /, *, filter: int = 0, group_type: Union[int, aiobungie.GroupType] = <GroupType.CLAN: 1>) -> collections.abc.Sequence[aiobungie.crates.clans.GroupMember]:
791    async def fetch_potential_groups_for_member(
792        self,
793        member_id: int,
794        member_type: typedefs.IntAnd[enums.MembershipType],
795        /,
796        *,
797        filter: int = 0,
798        group_type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
799    ) -> collections.Sequence[clans.GroupMember]:
800        """Fetch the potential groups for a clan member.
801
802        Parameters
803        ----------
804        member_id : `int`
805            The member's id
806        member_type : `aiobungie.typedefs.IntAnd[aiobungie.MembershipType]`
807            The member's membership type.
808
809        Other Parameters
810        ----------------
811        filter : `int`
812            Filter apply to list of joined groups. This Default to `0`
813        group_type : `aiobungie.typedefs.IntAnd[aiobungie.GroupType]`
814            The group's type.
815            This is always set to `aiobungie.GroupType.CLAN` and should not be changed.
816
817        Returns
818        -------
819        `collections.Sequence[aiobungie.crates.GroupMember]`
820            A sequence of joined potential groups for the fetched member.
821        """
822        resp = await self.rest.fetch_potential_groups_for_member(
823            member_id, member_type, filter=filter, group_type=group_type
824        )
825
826        return [
827            self.factory.deserialize_group_member(group) for group in resp["results"]
828        ]

Fetch the potential groups for a clan member.

Parameters
Other Parameters
Returns
async def fetch_clan_members( self, clan_id: int, /, *, name: Optional[str] = None, type: Union[int, aiobungie.MembershipType] = <MembershipType.NONE: 0>) -> aiobungie.Iterator[aiobungie.crates.clans.ClanMember]:
830    async def fetch_clan_members(
831        self,
832        clan_id: int,
833        /,
834        *,
835        name: typing.Optional[str] = None,
836        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.NONE,
837    ) -> iterators.Iterator[clans.ClanMember]:
838        """Fetch Bungie clan members.
839
840        Parameters
841        ----------
842        clan_id : `int`
843            The clans id
844
845        Other Parameters
846        ----------------
847        name : `typing.Optional[str]`
848            If provided, Only players matching this name will be returned.
849        type : `aiobungie.MembershipType`
850            An optional clan member's membership type.
851            This parameter is used to filter the returned results
852            by the provided membership, For an example XBox memberships only,
853            Otherwise will return all memberships.
854
855        Returns
856        -------
857        `aiobungie.iterators.Iterator[aiobungie.crates.ClanMember]`
858            An iterator over the bungie clan members.
859
860        Raises
861        ------
862        `aiobungie.NotFound`
863            The clan was not found.
864        """
865        resp = await self.rest.fetch_clan_members(clan_id, type=type, name=name)
866
867        return self.factory.deserialize_clan_members(resp)

Fetch Bungie clan members.

Parameters
  • clan_id (int): The clans id
Other Parameters
  • name (typing.Optional[str]): If provided, Only players matching this name will be returned.
  • type (aiobungie.MembershipType): An optional clan member's membership type. This parameter is used to filter the returned results by the provided membership, For an example XBox memberships only, Otherwise will return all memberships.
Returns
Raises
async def fetch_clan_banners(self) -> collections.abc.Sequence[aiobungie.crates.clans.ClanBanner]:
869    async def fetch_clan_banners(self) -> collections.Sequence[clans.ClanBanner]:
870        """Fetch the clan banners.
871
872        Returns
873        -------
874        `collections.Sequence[aiobungie.crates.ClanBanner]`
875            A sequence of the clan banners.
876        """
877        resp = await self.rest.fetch_clan_banners()
878
879        return self.factory.deserialize_clan_banners(resp)

Fetch the clan banners.

Returns
async def kick_clan_member( self, access_token: str, /, group_id: int, membership_id: int, membership_type: Union[int, aiobungie.MembershipType]) -> aiobungie.crates.clans.Clan:
882    async def kick_clan_member(
883        self,
884        access_token: str,
885        /,
886        group_id: int,
887        membership_id: int,
888        membership_type: typedefs.IntAnd[enums.MembershipType],
889    ) -> clans.Clan:
890        """Kick a member from the clan.
891
892        .. note::
893            This request requires OAuth2: oauth2: `AdminGroups` scope.
894
895        Parameters
896        ----------
897        access_token : `str`
898            The bearer access token associated with the bungie account.
899        group_id: `int`
900            The group id.
901        membership_id : `int`
902            The member id to kick.
903        membership_type : `aiobungie.typedefs.IntAnd[aiobungie.MembershipType]`
904            The member's membership type.
905
906        Returns
907        -------
908        `aiobungie.crates.clan.Clan`
909            The clan that the member was kicked from.
910        """
911        resp = await self.rest.kick_clan_member(
912            access_token,
913            group_id=group_id,
914            membership_id=membership_id,
915            membership_type=membership_type,
916        )
917
918        return self.factory.deserialize_clan(resp)

Kick a member from the clan.

This request requires OAuth2: oauth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group id.
  • membership_id (int): The member id to kick.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The member's membership type.
Returns
  • aiobungie.crates.clan.Clan: The clan that the member was kicked from.
async def fetch_clan_weekly_rewards(self, clan_id: int) -> aiobungie.crates.milestones.Milestone:
920    async def fetch_clan_weekly_rewards(self, clan_id: int) -> milestones.Milestone:
921        """Fetch a Bungie clan's weekly reward state.
922
923        Parameters
924        ----------
925        clan_id : `int`
926            The clan's id.
927
928        Returns
929        -------
930        `aiobungie.crates.Milestone`
931            A runtime status of the clan's milestone data.
932        """
933
934        resp = await self.rest.fetch_clan_weekly_rewards(clan_id)
935
936        return self.factory.deserialize_milestone(resp)

Fetch a Bungie clan's weekly reward state.

Parameters
  • clan_id (int): The clan's id.
Returns
async def fetch_inventory_item(self, hash: int, /) -> aiobungie.crates.entity.InventoryEntity:
940    async def fetch_inventory_item(self, hash: int, /) -> entity.InventoryEntity:
941        """Fetch a static inventory item entity given a its hash.
942
943        Parameters
944        ----------
945        hash: `int`
946            Inventory item's hash.
947
948        Returns
949        -------
950        `aiobungie.crates.InventoryEntity`
951            A bungie inventory item.
952        """
953        resp = await self.rest.fetch_inventory_item(hash)
954
955        return self.factory.deserialize_inventory_entity(resp)

Fetch a static inventory item entity given a its hash.

Parameters
  • hash (int): Inventory item's hash.
Returns
async def fetch_objective_entity(self, hash: int, /) -> aiobungie.crates.entity.ObjectiveEntity:
957    async def fetch_objective_entity(self, hash: int, /) -> entity.ObjectiveEntity:
958        """Fetch a Destiny objective entity given a its hash.
959
960        Parameters
961        ----------
962        hash: `int`
963            objective's hash.
964
965        Returns
966        -------
967        `aiobungie.crates.ObjectiveEntity`
968            An objective entity item.
969        """
970        resp = await self.rest.fetch_objective_entity(hash)
971
972        return self.factory.deserialize_objective_entity(resp)

Fetch a Destiny objective entity given a its hash.

Parameters
  • hash (int): objective's hash.
Returns
async def search_entities( self, name: str, entity_type: str, *, page: int = 0) -> aiobungie.Iterator[aiobungie.crates.entity.SearchableEntity]:
974    async def search_entities(
975        self, name: str, entity_type: str, *, page: int = 0
976    ) -> iterators.Iterator[entity.SearchableEntity]:
977        """Search for Destiny2 entities given a name and its type.
978
979        Parameters
980        ----------
981        name : `str`
982            The name of the entity, i.e., Thunderlord, One thousand voices.
983        entity_type : `str`
984            The type of the entity, AKA Definition,
985            For an example `DestinyInventoryItemDefinition` for emblems, weapons, and other inventory items.
986
987        Other Parameters
988        ----------------
989        page : `int`
990            An optional page to return. Default to 0.
991
992        Returns
993        -------
994        `aiobungie.iterators.Iterator[aiobungie.crates.SearchableEntity]`
995            An iterator over the found results matching the provided name.
996        """
997        resp = await self.rest.search_entities(name, entity_type, page=page)
998
999        return self.factory.deserialize_inventory_results(resp)

Search for Destiny2 entities given a name and its type.

Parameters
  • name (str): The name of the entity, i.e., Thunderlord, One thousand voices.
  • entity_type (str): The type of the entity, AKA Definition, For an example DestinyInventoryItemDefinition for emblems, weapons, and other inventory items.
Other Parameters
  • page (int): An optional page to return. Default to 0.
Returns
async def fetch_fireteams( self, activity_type: Union[int, aiobungie.FireteamActivity], *, platform: Union[int, aiobungie.FireteamPlatform] = <FireteamPlatform.ANY: 0>, language: Union[aiobungie.FireteamLanguage, str] = <FireteamLanguage.ALL: >, date_range: int = 0, page: int = 0, slots_filter: int = 0) -> Optional[collections.abc.Sequence[aiobungie.crates.fireteams.Fireteam]]:
1003    async def fetch_fireteams(
1004        self,
1005        activity_type: typedefs.IntAnd[fireteams.FireteamActivity],
1006        *,
1007        platform: typedefs.IntAnd[
1008            fireteams.FireteamPlatform
1009        ] = fireteams.FireteamPlatform.ANY,
1010        language: typing.Union[
1011            fireteams.FireteamLanguage, str
1012        ] = fireteams.FireteamLanguage.ALL,
1013        date_range: int = 0,
1014        page: int = 0,
1015        slots_filter: int = 0,
1016    ) -> typing.Optional[collections.Sequence[fireteams.Fireteam]]:
1017        """Fetch public Bungie fireteams with open slots.
1018
1019        Parameters
1020        ----------
1021        activity_type : `aiobungie.typedefs.IntAnd[aiobungie.crates.FireteamActivity]`
1022            The fireteam activity type.
1023
1024        Other Parameters
1025        ----------------
1026        platform : `aiobungie.typedefs.IntAnd[aiobungie.crates.fireteams.FireteamPlatform]`
1027            If this is provided. Then the results will be filtered with the given platform.
1028            Defaults to `aiobungie.crates.FireteamPlatform.ANY` which returns all platforms.
1029        language : `typing.Union[aiobungie.crates.fireteams.FireteamLanguage, str]`
1030            A locale language to filter the used language in that fireteam.
1031            Defaults to `aiobungie.crates.FireteamLanguage.ALL`
1032        date_range : `int`
1033            An integer to filter the date range of the returned fireteams. Defaults to `aiobungie.FireteamDate.ALL`.
1034        page : `int`
1035            The page number. By default its `0` which returns all available activities.
1036        slots_filter : `int`
1037            Filter the returned fireteams based on available slots. Default is `0`
1038
1039        Returns
1040        -------
1041        `typing.Optional[collections.Sequence[fireteams.Fireteam]]`
1042            A sequence of `aiobungie.crates.Fireteam` or `None`.
1043        """
1044
1045        resp = await self.rest.fetch_fireteams(
1046            activity_type,
1047            platform=platform,
1048            language=language,
1049            date_range=date_range,
1050            page=page,
1051            slots_filter=slots_filter,
1052        )
1053
1054        return self.factory.deserialize_fireteams(resp)

Fetch public Bungie fireteams with open slots.

Parameters
Other Parameters
  • platform (aiobungie.typedefs.IntAnd[aiobungie.FireteamPlatform]): If this is provided. Then the results will be filtered with the given platform. Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
  • language (typing.Union[aiobungie.FireteamLanguage, str]): A locale language to filter the used language in that fireteam. Defaults to aiobungie.crates.FireteamLanguage.ALL
  • date_range (int): An integer to filter the date range of the returned fireteams. Defaults to aiobungie.FireteamDate.ALL.
  • page (int): The page number. By default its 0 which returns all available activities.
  • slots_filter (int): Filter the returned fireteams based on available slots. Default is 0
Returns
async def fetch_avaliable_clan_fireteams( self, access_token: str, group_id: int, activity_type: Union[int, aiobungie.FireteamActivity], *, platform: Union[int, aiobungie.FireteamPlatform], language: Union[aiobungie.FireteamLanguage, str], date_range: int = 0, page: int = 0, public_only: bool = False, slots_filter: int = 0) -> Optional[collections.abc.Sequence[aiobungie.crates.fireteams.Fireteam]]:
1056    async def fetch_avaliable_clan_fireteams(
1057        self,
1058        access_token: str,
1059        group_id: int,
1060        activity_type: typedefs.IntAnd[fireteams.FireteamActivity],
1061        *,
1062        platform: typedefs.IntAnd[fireteams.FireteamPlatform],
1063        language: typing.Union[fireteams.FireteamLanguage, str],
1064        date_range: int = 0,
1065        page: int = 0,
1066        public_only: bool = False,
1067        slots_filter: int = 0,
1068    ) -> typing.Optional[collections.Sequence[fireteams.Fireteam]]:
1069        """Fetch a clan's fireteams with open slots.
1070
1071        .. note::
1072            This method requires OAuth2: ReadGroups scope.
1073
1074        Parameters
1075        ----------
1076        access_token : `str`
1077            The bearer access token associated with the bungie account.
1078        group_id : `int`
1079            The group/clan id of the fireteam.
1080        activity_type : `aiobungie.typedefs.IntAnd[aiobungie.crates.FireteamActivity]`
1081            The fireteam activity type.
1082
1083        Other Parameters
1084        ----------------
1085        platform : `aiobungie.typedefs.IntAnd[aiobungie.crates.fireteams.FireteamPlatform]`
1086            If this is provided. Then the results will be filtered with the given platform.
1087            Defaults to `aiobungie.crates.FireteamPlatform.ANY` which returns all platforms.
1088        language : `typing.Union[aiobungie.crates.fireteams.FireteamLanguage, str]`
1089            A locale language to filter the used language in that fireteam.
1090            Defaults to `aiobungie.crates.FireteamLanguage.ALL`
1091        date_range : `int`
1092            An integer to filter the date range of the returned fireteams. Defaults to `0`.
1093        page : `int`
1094            The page number. By default its `0` which returns all available activities.
1095        public_only: `bool`
1096            If set to True, Then only public fireteams will be returned.
1097        slots_filter : `int`
1098            Filter the returned fireteams based on available slots. Default is `0`
1099
1100        Returns
1101        -------
1102        `typing.Optional[collections.Sequence[aiobungie.crates.Fireteam]]`
1103            A sequence of  fireteams found in the clan.
1104            `None` will be returned if nothing was found.
1105        """
1106        resp = await self.rest.fetch_avaliable_clan_fireteams(
1107            access_token,
1108            group_id,
1109            activity_type,
1110            platform=platform,
1111            language=language,
1112            date_range=date_range,
1113            page=page,
1114            public_only=public_only,
1115            slots_filter=slots_filter,
1116        )
1117
1118        return self.factory.deserialize_fireteams(resp)

Fetch a clan's fireteams with open slots.

This method requires OAuth2: ReadGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group/clan id of the fireteam.
  • activity_type (aiobungie.typedefs.IntAnd[aiobungie.crates.FireteamActivity]): The fireteam activity type.
Other Parameters
  • platform (aiobungie.typedefs.IntAnd[aiobungie.FireteamPlatform]): If this is provided. Then the results will be filtered with the given platform. Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
  • language (typing.Union[aiobungie.FireteamLanguage, str]): A locale language to filter the used language in that fireteam. Defaults to aiobungie.crates.FireteamLanguage.ALL
  • date_range (int): An integer to filter the date range of the returned fireteams. Defaults to 0.
  • page (int): The page number. By default its 0 which returns all available activities.
  • public_only (bool): If set to True, Then only public fireteams will be returned.
  • slots_filter (int): Filter the returned fireteams based on available slots. Default is 0
Returns
  • typing.Optional[collections.Sequence[aiobungie.crates.Fireteam]]: A sequence of fireteams found in the clan. None will be returned if nothing was found.
async def fetch_clan_fireteam( self, access_token: str, fireteam_id: int, group_id: int) -> aiobungie.crates.fireteams.AvailableFireteam:
1120    async def fetch_clan_fireteam(
1121        self, access_token: str, fireteam_id: int, group_id: int
1122    ) -> fireteams.AvailableFireteam:
1123        """Fetch a specific clan fireteam.
1124
1125        .. note::
1126            This method requires OAuth2: ReadGroups scope.
1127
1128        Parameters
1129        ----------
1130        access_token : `str`
1131            The bearer access token associated with the bungie account.
1132        group_id : `int`
1133            The group/clan id to fetch the fireteam from.
1134        fireteam_id : `int`
1135            The fireteam id to fetch.
1136
1137        Returns
1138        -------
1139        `typing.Optional[aiobungie.crates.AvailableFireteam]`
1140            A sequence of available fireteams objects if exists. else `None` will be returned.
1141        """
1142        resp = await self.rest.fetch_clan_fireteam(access_token, fireteam_id, group_id)
1143
1144        return self.factory.deserialize_available_fireteams(
1145            resp, no_results=True
1146        )  # type: ignore[return-value]

Fetch a specific clan fireteam.

This method requires OAuth2: ReadGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group/clan id to fetch the fireteam from.
  • fireteam_id (int): The fireteam id to fetch.
Returns
async def fetch_my_clan_fireteams( self, access_token: str, group_id: int, *, include_closed: bool = True, platform: Union[int, aiobungie.FireteamPlatform], language: Union[aiobungie.FireteamLanguage, str], filtered: bool = True, page: int = 0) -> collections.abc.Sequence[aiobungie.crates.fireteams.AvailableFireteam]:
1148    async def fetch_my_clan_fireteams(
1149        self,
1150        access_token: str,
1151        group_id: int,
1152        *,
1153        include_closed: bool = True,
1154        platform: typedefs.IntAnd[fireteams.FireteamPlatform],
1155        language: typing.Union[fireteams.FireteamLanguage, str],
1156        filtered: bool = True,
1157        page: int = 0,
1158    ) -> collections.Sequence[fireteams.AvailableFireteam]:
1159        """A method that's similar to `fetch_fireteams` but requires OAuth2.
1160
1161        .. note::
1162            This method requires OAuth2: ReadGroups scope.
1163
1164        Parameters
1165        ----------
1166        access_token : str
1167            The bearer access token associated with the bungie account.
1168        group_id : int
1169            The group/clan id to fetch.
1170
1171        Other Parameters
1172        ----------------
1173        include_closed : bool
1174            If provided and set to True, It will also return closed fireteams.
1175            If provided and set to False, It will only return public fireteams. Default is True.
1176        platform : aiobungie.typedefs.IntAnd[aiobungie.crates.fireteams.FireteamPlatform]
1177            If this is provided. Then the results will be filtered with the given platform.
1178            Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
1179        language : typing.Union[aiobungie.crates.fireteams.FireteamLanguage, str]
1180            A locale language to filter the used language in that fireteam.
1181            Defaults to aiobungie.crates.FireteamLanguage.ALL
1182        filtered : bool
1183            If set to True, it will filter by clan. Otherwise not. Default is True.
1184        page : int
1185            The page number. By default its 0 which returns all available activities.
1186
1187        Returns
1188        -------
1189        `collections.Sequence[aiobungie.crates.AvailableFireteam]`
1190            A sequence of available fireteams objects if exists. else `None` will be returned.
1191        """
1192        resp = await self.rest.fetch_my_clan_fireteams(
1193            access_token,
1194            group_id,
1195            include_closed=include_closed,
1196            platform=platform,
1197            language=language,
1198            filtered=filtered,
1199            page=page,
1200        )
1201
1202        return self.factory.deserialize_available_fireteams(resp)  # type: ignore[return-value]

A method that's similar to fetch_fireteams but requires OAuth2.

This method requires OAuth2: ReadGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group/clan id to fetch.
Other Parameters
  • include_closed (bool): If provided and set to True, It will also return closed fireteams. If provided and set to False, It will only return public fireteams. Default is True.
  • platform (aiobungie.typedefs.IntAnd[aiobungie.FireteamPlatform]): If this is provided. Then the results will be filtered with the given platform. Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
  • language (typing.Union[aiobungie.FireteamLanguage, str]): A locale language to filter the used language in that fireteam. Defaults to aiobungie.crates.FireteamLanguage.ALL
  • filtered (bool): If set to True, it will filter by clan. Otherwise not. Default is True.
  • page (int): The page number. By default its 0 which returns all available activities.
Returns
async def fetch_friends( self, access_token: str, /) -> collections.abc.Sequence[aiobungie.crates.friends.Friend]:
1206    async def fetch_friends(
1207        self, access_token: str, /
1208    ) -> collections.Sequence[friends.Friend]:
1209        """Fetch bungie friend list.
1210
1211        .. note::
1212            This requests OAuth2: ReadUserData scope.
1213
1214        Parameters
1215        -----------
1216        access_token : `str`
1217            The bearer access token associated with the bungie account.
1218
1219        Returns
1220        -------
1221        `collections.Sequence[aiobungie.crates.Friend]`
1222            A sequence of the friends associated with that access token.
1223        """
1224
1225        resp = await self.rest.fetch_friends(access_token)
1226
1227        return self.factory.deserialize_friends(resp)

Fetch bungie friend list.

This requests OAuth2: ReadUserData scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
Returns
async def fetch_friend_requests(self, access_token: str, /) -> aiobungie.crates.friends.FriendRequestView:
1229    async def fetch_friend_requests(
1230        self, access_token: str, /
1231    ) -> friends.FriendRequestView:
1232        """Fetch pending bungie friend requests queue.
1233
1234        .. note::
1235            This requests OAuth2: ReadUserData scope.
1236
1237        Parameters
1238        -----------
1239        access_token : `str`
1240            The bearer access token associated with the bungie account.
1241
1242        Returns
1243        -------
1244        `aiobungie.crates.FriendRequestView`
1245            A friend requests view of that associated access token.
1246        """
1247
1248        resp = await self.rest.fetch_friend_requests(access_token)
1249
1250        return self.factory.deserialize_friend_requests(resp)

Fetch pending bungie friend requests queue.

This requests OAuth2: ReadUserData scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
Returns
async def fetch_application(self, appid: int, /) -> aiobungie.crates.application.Application:
1254    async def fetch_application(self, appid: int, /) -> application.Application:
1255        """Fetch a Bungie application.
1256
1257        Parameters
1258        -----------
1259        appid: `int`
1260            The application id.
1261
1262        Returns
1263        --------
1264        `aiobungie.crates.Application`
1265            A Bungie application.
1266        """
1267        resp = await self.rest.fetch_application(appid)
1268
1269        return self.factory.deserialize_app(resp)

Fetch a Bungie application.

Parameters
  • appid (int): The application id.
Returns
async def fetch_public_milestone_content( self, milestone_hash: int, /) -> aiobungie.crates.milestones.MilestoneContent:
1273    async def fetch_public_milestone_content(
1274        self, milestone_hash: int, /
1275    ) -> milestones.MilestoneContent:
1276        """Fetch the milestone content given its hash.
1277
1278        Parameters
1279        ----------
1280        milestone_hash : `int`
1281            The milestone hash.
1282
1283        Returns
1284        -------
1285        `aiobungie.crates.milestones.MilestoneContent`
1286            A milestone content object.
1287        """
1288        resp = await self.rest.fetch_public_milestone_content(milestone_hash)
1289
1290        return self.factory.deserialize_public_milestone_content(resp)

Fetch the milestone content given its hash.

Parameters
  • milestone_hash (int): The milestone hash.
Returns
  • aiobungie.crates.milestones.MilestoneContent: A milestone content object.
@typing.final
class ClosedReasons(aiobungie.Flag):
779@typing.final
780class ClosedReasons(Flag):
781    """A Flags enumeration representing the reasons why a person can't join this user's fireteam."""
782
783    NONE = 0
784    MATCHMAKING = 1 << 0
785    LOADING = 1 << 1
786    SOLO = 1 << 2
787    """The activity is required to be played solo."""
788    INTERNAL_REASONS = 1 << 3
789    """
790    The user can't be joined for one of a variety of internal reasons.
791    Basically, the game can't let you join at this time,
792    but for reasons that aren't under the control of this user
793    """
794    DISALLOWED_BY_GAME_STATE = 1 << 4
795    """The user's current activity/quest/other transitory game state is preventing joining."""
796    OFFLINE = 32768
797    """The user appears offline."""

A Flags enumeration representing the reasons why a person can't join this user's fireteam.

NONE = <ClosedReasons.NONE: 0>
MATCHMAKING = <ClosedReasons.MATCHMAKING: 1>
LOADING = <ClosedReasons.LOADING: 2>
SOLO = <ClosedReasons.SOLO: 4>

The activity is required to be played solo.

INTERNAL_REASONS = <ClosedReasons.INTERNAL_REASONS: 8>

The user can't be joined for one of a variety of internal reasons. Basically, the game can't let you join at this time, but for reasons that aren't under the control of this user

DISALLOWED_BY_GAME_STATE = <ClosedReasons.DISALLOWED_BY_GAME_STATE: 16>

The user's current activity/quest/other transitory game state is preventing joining.

OFFLINE = <ClosedReasons.OFFLINE: 32768>

The user appears offline.

Inherited Members
Flag
name
value
@typing.final
class ComponentFields(aiobungie.Enum):
74@typing.final
75class ComponentFields(enums.Enum):
76    """An enum that provides fields found in a base component response."""
77
78    PRIVACY = ComponentPrivacy
79    DISABLED = False

An enum that provides fields found in a base component response.

PRIVACY = <ComponentFields.PRIVACY: <enum 'ComponentPrivacy'>>
DISABLED = <ComponentFields.DISABLED: False>
Inherited Members
Enum
name
value
@typing.final
class ComponentPrivacy(builtins.int, aiobungie.Enum):
65@typing.final
66class ComponentPrivacy(int, enums.Enum):
67    """An enum the provides privacy settings for profile components."""
68
69    NONE = 0
70    PUBLIC = 1
71    PRIVATE = 2

An enum the provides privacy settings for profile components.

NONE = <ComponentPrivacy.NONE: 0>
PUBLIC = <ComponentPrivacy.PUBLIC: 1>
PRIVATE = <ComponentPrivacy.PRIVATE: 2>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class ComponentType(aiobungie.Enum):
358@typing.final
359class ComponentType(Enum):
360    """An Enum for Destiny 2 profile Components."""
361
362    NONE = 0
363
364    PROFILE = 100
365    PROFILE_INVENTORIES = 102
366    PROFILE_CURRENCIES = 103
367    PROFILE_PROGRESSION = 104
368    ALL_PROFILES = (
369        PROFILE,
370        PROFILE_INVENTORIES,
371        PROFILE_CURRENCIES,
372        PROFILE_PROGRESSION,
373    )
374    """All profile components."""
375
376    VENDORS = 400
377    VENDOR_SALES = 402
378    VENDOR_RECEIPTS = 101
379    ALL_VENDORS = (VENDORS, VENDOR_RECEIPTS, VENDOR_SALES)
380    """All vendor components."""
381
382    # Items
383    ITEM_INSTANCES = 300
384    ITEM_OBJECTIVES = 301
385    ITEM_PERKS = 302
386    ITEM_RENDER_DATA = 303
387    ITEM_STATS = 304
388    ITEM_SOCKETS = 305
389    ITEM_TALENT_GRINDS = 306
390    ITEM_PLUG_STATES = 308
391    ITEM_PLUG_OBJECTIVES = 309
392    ITEM_REUSABLE_PLUGS = 310
393
394    ALL_ITEMS = (
395        ITEM_PLUG_OBJECTIVES,
396        ITEM_PLUG_STATES,
397        ITEM_SOCKETS,
398        ITEM_INSTANCES,
399        ITEM_OBJECTIVES,
400        ITEM_PERKS,
401        ITEM_RENDER_DATA,
402        ITEM_STATS,
403        ITEM_TALENT_GRINDS,
404        ITEM_REUSABLE_PLUGS,
405    )
406    """All item components."""
407
408    PLATFORM_SILVER = 105
409    KIOSKS = 500
410    CURRENCY_LOOKUPS = 600
411    PRESENTATION_NODES = 700
412    COLLECTIBLES = 800
413    RECORDS = 900
414    TRANSITORY = 1000
415    METRICS = 1100
416    INVENTORIES = 102
417    STRING_VARIABLES = 1200
418    CRAFTABLES = 1300
419
420    CHARACTERS = 200
421    CHARACTER_INVENTORY = 201
422    CHARECTER_PROGRESSION = 202
423    CHARACTER_RENDER_DATA = 203
424    CHARACTER_ACTIVITIES = 204
425    CHARACTER_EQUIPMENT = 205
426
427    ALL_CHARACTERS = (
428        CHARACTERS,
429        CHARACTER_INVENTORY,
430        CHARECTER_PROGRESSION,
431        CHARACTER_RENDER_DATA,
432        CHARACTER_ACTIVITIES,
433        CHARACTER_EQUIPMENT,
434        RECORDS,
435    )
436    """All character components."""
437
438    ALL = (
439        *ALL_PROFILES,  # type: ignore
440        *ALL_CHARACTERS,  # type: ignore
441        *ALL_VENDORS,  # type: ignore
442        *ALL_ITEMS,  # type: ignore
443        RECORDS,
444        CURRENCY_LOOKUPS,
445        PRESENTATION_NODES,
446        COLLECTIBLES,
447        KIOSKS,
448        METRICS,
449        PLATFORM_SILVER,
450        INVENTORIES,
451        STRING_VARIABLES,
452        TRANSITORY,
453        CRAFTABLES,
454    )
455    """ALl components included."""

An Enum for Destiny 2 profile Components.

NONE = <ComponentType.NONE: 0>
PROFILE = <ComponentType.PROFILE: 100>
PROFILE_INVENTORIES = <ComponentType.PROFILE_INVENTORIES: 102>
PROFILE_CURRENCIES = <ComponentType.PROFILE_CURRENCIES: 103>
PROFILE_PROGRESSION = <ComponentType.PROFILE_PROGRESSION: 104>
ALL_PROFILES = <ComponentType.ALL_PROFILES: (100, 102, 103, 104)>

All profile components.

VENDORS = <ComponentType.VENDORS: 400>
VENDOR_SALES = <ComponentType.VENDOR_SALES: 402>
VENDOR_RECEIPTS = <ComponentType.VENDOR_RECEIPTS: 101>
ALL_VENDORS = <ComponentType.ALL_VENDORS: (400, 101, 402)>

All vendor components.

ITEM_INSTANCES = <ComponentType.ITEM_INSTANCES: 300>
ITEM_OBJECTIVES = <ComponentType.ITEM_OBJECTIVES: 301>
ITEM_PERKS = <ComponentType.ITEM_PERKS: 302>
ITEM_RENDER_DATA = <ComponentType.ITEM_RENDER_DATA: 303>
ITEM_STATS = <ComponentType.ITEM_STATS: 304>
ITEM_SOCKETS = <ComponentType.ITEM_SOCKETS: 305>
ITEM_TALENT_GRINDS = <ComponentType.ITEM_TALENT_GRINDS: 306>
ITEM_PLUG_STATES = <ComponentType.ITEM_PLUG_STATES: 308>
ITEM_PLUG_OBJECTIVES = <ComponentType.ITEM_PLUG_OBJECTIVES: 309>
ITEM_REUSABLE_PLUGS = <ComponentType.ITEM_REUSABLE_PLUGS: 310>
ALL_ITEMS = <ComponentType.ALL_ITEMS: (309, 308, 305, 300, 301, 302, 303, 304, 306, 310)>

All item components.

PLATFORM_SILVER = <ComponentType.PLATFORM_SILVER: 105>
KIOSKS = <ComponentType.KIOSKS: 500>
CURRENCY_LOOKUPS = <ComponentType.CURRENCY_LOOKUPS: 600>
PRESENTATION_NODES = <ComponentType.PRESENTATION_NODES: 700>
COLLECTIBLES = <ComponentType.COLLECTIBLES: 800>
RECORDS = <ComponentType.RECORDS: 900>
TRANSITORY = <ComponentType.TRANSITORY: 1000>
METRICS = <ComponentType.METRICS: 1100>
INVENTORIES = <ComponentType.PROFILE_INVENTORIES: 102>
STRING_VARIABLES = <ComponentType.STRING_VARIABLES: 1200>
CRAFTABLES = <ComponentType.CRAFTABLES: 1300>
CHARACTERS = <ComponentType.CHARACTERS: 200>
CHARACTER_INVENTORY = <ComponentType.CHARACTER_INVENTORY: 201>
CHARECTER_PROGRESSION = <ComponentType.CHARECTER_PROGRESSION: 202>
CHARACTER_RENDER_DATA = <ComponentType.CHARACTER_RENDER_DATA: 203>
CHARACTER_ACTIVITIES = <ComponentType.CHARACTER_ACTIVITIES: 204>
CHARACTER_EQUIPMENT = <ComponentType.CHARACTER_EQUIPMENT: 205>
ALL_CHARACTERS = <ComponentType.ALL_CHARACTERS: (200, 201, 202, 203, 204, 205, 900)>

All character components.

ALL = <ComponentType.ALL: (100, 102, 103, 104, 200, 201, 202, 203, 204, 205, 900, 400, 101, 402, 309, 308, 305, 300, 301, 302, 303, 304, 306, 310, 900, 600, 700, 800, 500, 1100, 105, 102, 1200, 1000, 1300)>

ALl components included.

Inherited Members
Enum
name
value
@typing.final
class CredentialType(builtins.int, aiobungie.Enum):
661@typing.final
662class CredentialType(int, Enum):
663    """The types of the accounts system supports at bungie."""
664
665    NONE = 0
666    XUID = 1
667    PSNID = 2
668    WILD = 3
669    FAKE = 4
670    FACEBOOK = 5
671    GOOGLE = 8
672    WINDOWS = 9
673    DEMONID = 10
674    STEAMID = 12
675    BATTLENETID = 14
676    STADIAID = 16
677    TWITCHID = 18

The types of the accounts system supports at bungie.

NONE = <CredentialType.NONE: 0>
XUID = <CredentialType.XUID: 1>
PSNID = <CredentialType.PSNID: 2>
WILD = <CredentialType.WILD: 3>
FAKE = <CredentialType.FAKE: 4>
FACEBOOK = <CredentialType.FACEBOOK: 5>
GOOGLE = <CredentialType.GOOGLE: 8>
WINDOWS = <CredentialType.WINDOWS: 9>
DEMONID = <CredentialType.DEMONID: 10>
STEAMID = <CredentialType.STEAMID: 12>
BATTLENETID = <CredentialType.BATTLENETID: 14>
STADIAID = <CredentialType.STADIAID: 16>
TWITCHID = <CredentialType.TWITCHID: 18>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class DamageType(builtins.int, aiobungie.Enum):
539@typing.final
540class DamageType(int, Enum):
541    """Enums for Destiny Damage types"""
542
543    NONE = 0
544    KINETIC = 1
545    ARC = 2
546    SOLAR = 3
547    VOID = 4
548    RAID = 5
549    """This is a special damage type reserved for some raid activity encounters."""
550    STASIS = 6

Enums for Destiny Damage types

NONE = <DamageType.NONE: 0>
KINETIC = <DamageType.KINETIC: 1>
ARC = <DamageType.ARC: 2>
SOLAR = <DamageType.SOLAR: 3>
VOID = <DamageType.VOID: 4>
RAID = <DamageType.RAID: 5>

This is a special damage type reserved for some raid activity encounters.

STASIS = <DamageType.STASIS: 6>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Difficulty(builtins.int, aiobungie.Enum):
64@typing.final
65class Difficulty(int, enums.Enum):
66    """An enum for activities difficulties."""
67
68    TRIVIAL = 0
69    EASY = 1
70    NORMAL = 2
71    CHALLENGING = 3
72    HARD = 4
73    BRAVE = 5
74    ALMOST_IMPOSSIBLE = 6
75    IMPOSSIBLE = 7

An enum for activities difficulties.

TRIVIAL = <Difficulty.TRIVIAL: 0>
EASY = <Difficulty.EASY: 1>
NORMAL = <Difficulty.NORMAL: 2>
CHALLENGING = <Difficulty.CHALLENGING: 3>
HARD = <Difficulty.HARD: 4>
BRAVE = <Difficulty.BRAVE: 5>
ALMOST_IMPOSSIBLE = <Difficulty.ALMOST_IMPOSSIBLE: 6>
IMPOSSIBLE = <Difficulty.IMPOSSIBLE: 7>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Dungeon(builtins.int, aiobungie.Enum):
160@typing.final
161class Dungeon(int, Enum):
162    """An Enum for all available Dungeon/Like missions in Destiny 2."""
163
164    NORMAL_PRESAGE = 2124066889
165    """Normal Presage"""
166
167    MASTER_PRESAGE = 4212753278
168    """Master Presage"""
169
170    HARBINGER = 1738383283
171    """Harbinger"""
172
173    PROPHECY = 4148187374
174    """Prophecy"""
175
176    MASTER_POH = 785700673
177    """Master Pit of Heresy?"""
178
179    LEGEND_POH = 785700678
180    """Legend Pit of Heresy?"""
181
182    POH = 1375089621
183    """Normal Pit of Heresy."""
184
185    SHATTERED = 2032534090
186    """Shattered Throne"""
187
188    GOA_LEGEND = 4078656646
189    """Grasp of Avarice legend."""
190
191    GOA_MASTER = 3774021532
192    """Grasp of Avarice master."""

An Enum for all available Dungeon/Like missions in Destiny 2.

NORMAL_PRESAGE = <Dungeon.NORMAL_PRESAGE: 2124066889>

Normal Presage

MASTER_PRESAGE = <Dungeon.MASTER_PRESAGE: 4212753278>

Master Presage

HARBINGER = <Dungeon.HARBINGER: 1738383283>

Harbinger

PROPHECY = <Dungeon.PROPHECY: 4148187374>

Prophecy

MASTER_POH = <Dungeon.MASTER_POH: 785700673>

Master Pit of Heresy?

LEGEND_POH = <Dungeon.LEGEND_POH: 785700678>

Legend Pit of Heresy?

POH = <Dungeon.POH: 1375089621>

Normal Pit of Heresy.

SHATTERED = <Dungeon.SHATTERED: 2032534090>

Shattered Throne

GOA_LEGEND = <Dungeon.GOA_LEGEND: 4078656646>

Grasp of Avarice legend.

GOA_MASTER = <Dungeon.GOA_MASTER: 3774021532>

Grasp of Avarice master.

Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
class Enum(enum.Enum):
72class Enum(__enum.Enum):
73    """Builtin Python enum with extra handlings."""
74
75    @property
76    def name(self) -> str:  # type: ignore[override]
77        return self._name_
78
79    @property
80    def value(self) -> typing.Any:  # type: ignore[override]
81        return self._value_
82
83    def __str__(self) -> str:
84        return self._name_
85
86    def __repr__(self) -> str:
87        return f"<{type(self).__name__}.{self._name_}: {self._value_!s}>"
88
89    def __int__(self) -> int:
90        if isinstance(self.value, _ITERABLE):
91            raise TypeError(
92                f"Can't overload {self.value} in {type(self).__name__}, Please use `.value` attribute.",
93            )
94        return int(self.value)

Builtin Python enum with extra handlings.

name: str

The name of the Enum member.

value: Any

The value of the Enum member.

class Factory(aiobungie.interfaces.factory.FactoryInterface):
  61class Factory(interfaces.FactoryInterface):
  62    """The base deserialization factory class for all aiobungie objects.
  63
  64    Highly inspired hikari entity factory used to deserialize JSON responses from the REST client and turning them
  65    into a `aiobungie.crates` Python classes.
  66    """
  67
  68    __slots__ = ("_net",)
  69
  70    def __init__(self, net: traits.Netrunner) -> None:
  71        self._net = net
  72
  73    def deserialize_bungie_user(self, data: typedefs.JSONObject) -> user.BungieUser:
  74        return user.BungieUser(
  75            id=int(data["membershipId"]),
  76            created_at=time.clean_date(data["firstAccess"]),
  77            name=data.get("cachedBungieGlobalDisplayName", undefined.UNDEFINED),
  78            is_deleted=data["isDeleted"],
  79            about=data["about"],
  80            updated_at=time.clean_date(data["lastUpdate"]),
  81            psn_name=data.get("psnDisplayName", None),
  82            stadia_name=data.get("stadiaDisplayName", None),
  83            steam_name=data.get("steamDisplayName", None),
  84            twitch_name=data.get("twitchDisplayName", None),
  85            blizzard_name=data.get("blizzardDisplayName", None),
  86            status=data["statusText"],
  87            locale=data["locale"],
  88            picture=assets.Image(path=str(data["profilePicturePath"])),
  89            code=data.get("cachedBungieGlobalDisplayNameCode", None),
  90            unique_name=data.get("uniqueName", None),
  91            theme_id=int(data["profileTheme"]),
  92            show_activity=bool(data["showActivity"]),
  93            theme_name=data["profileThemeName"],
  94            display_title=data["userTitleDisplay"],
  95        )
  96
  97    def deserialize_partial_bungie_user(
  98        self, payload: typedefs.JSONObject
  99    ) -> user.PartialBungieUser:
 100        return user.PartialBungieUser(
 101            net=self._net,
 102            types=[
 103                enums.MembershipType(type_)
 104                for type_ in payload.get("applicableMembershipTypes", [])
 105            ],
 106            name=payload.get("displayName", undefined.UNDEFINED),
 107            id=int(payload["membershipId"]),
 108            crossave_override=enums.MembershipType(payload["crossSaveOverride"]),
 109            is_public=payload["isPublic"],
 110            icon=assets.Image(payload.get("iconPath", "")),
 111            type=enums.MembershipType(payload["membershipType"]),
 112        )
 113
 114    def deserialize_destiny_membership(
 115        self, payload: typedefs.JSONObject
 116    ) -> user.DestinyMembership:
 117        name: undefined.UndefinedOr[str] = undefined.UNDEFINED
 118        if (
 119            raw_name := payload.get("bungieGlobalDisplayName", "")
 120        ) and not typedefs.is_unknown(raw_name):
 121            name = raw_name
 122
 123        return user.DestinyMembership(
 124            net=self._net,
 125            id=int(payload["membershipId"]),
 126            name=name,
 127            code=payload.get("bungieGlobalDisplayNameCode", None),
 128            last_seen_name=payload.get("LastSeenDisplayName")
 129            or payload.get("displayName")  # noqa: W503
 130            or "",  # noqa: W503
 131            type=enums.MembershipType(payload["membershipType"]),
 132            is_public=payload["isPublic"],
 133            crossave_override=enums.MembershipType(payload["crossSaveOverride"]),
 134            icon=assets.Image(payload.get("iconPath", "")),
 135            types=[
 136                enums.MembershipType(type_)
 137                for type_ in payload.get("applicableMembershipTypes", [])
 138            ],
 139        )
 140
 141    def deserialize_destiny_memberships(
 142        self, data: typedefs.JSONArray
 143    ) -> collections.Sequence[user.DestinyMembership]:
 144        return [self.deserialize_destiny_membership(membership) for membership in data]
 145
 146    def deserialize_user(self, data: typedefs.JSONObject) -> user.User:
 147
 148        primary_membership_id: typing.Optional[int] = None
 149        if raw_primary_id := data.get("primaryMembershipId"):
 150            primary_membership_id = int(raw_primary_id)
 151
 152        return user.User(
 153            bungie=self.deserialize_bungie_user(data["bungieNetUser"]),
 154            destiny=self.deserialize_destiny_memberships(data["destinyMemberships"]),
 155            primary_membership_id=primary_membership_id,
 156        )
 157
 158    def deserialize_searched_user(
 159        self, payload: typedefs.JSONObject
 160    ) -> user.SearchableDestinyUser:
 161        name: undefined.UndefinedOr[str] = undefined.UNDEFINED
 162        if (raw_name := payload["bungieGlobalDisplayName"]) and not typedefs.is_unknown(
 163            raw_name
 164        ):
 165            name = raw_name
 166
 167        code: typing.Optional[int] = None
 168        if raw_code := payload.get("bungieGlobalDisplayNameCode"):
 169            code = int(raw_code)
 170
 171        bungie_id: typing.Optional[int] = None
 172        if raw_bungie_id := payload.get("bungieNetMembershipId"):
 173            bungie_id = int(raw_bungie_id)
 174
 175        return user.SearchableDestinyUser(
 176            name=name,
 177            code=code,
 178            bungie_id=bungie_id,
 179            memberships=self.deserialize_destiny_memberships(
 180                payload["destinyMemberships"]
 181            ),
 182        )
 183
 184    def deserialize_user_credentials(
 185        self, payload: typedefs.JSONArray
 186    ) -> collections.Sequence[user.UserCredentials]:
 187        return [
 188            user.UserCredentials(
 189                type=enums.CredentialType(int(creds["credentialType"])),
 190                display_name=creds["credentialDisplayName"],
 191                is_public=creds["isPublic"],
 192                self_as_string=creds.get("credentialAsString", undefined.UNDEFINED),
 193            )
 194            for creds in payload
 195        ]
 196
 197    def deserialize_user_themes(
 198        self, payload: typedefs.JSONArray
 199    ) -> collections.Sequence[user.UserThemes]:
 200        return [
 201            user.UserThemes(
 202                id=int(entry["userThemeId"]),
 203                name=entry["userThemeName"]
 204                if "userThemeName" in entry
 205                else undefined.UNDEFINED,
 206                description=entry["userThemeDescription"]
 207                if "userThemeDescription" in entry
 208                else undefined.UNDEFINED,
 209            )
 210            for entry in payload
 211        ]
 212
 213    def deserialize_clan(self, payload: typedefs.JSONObject) -> clans.Clan:
 214
 215        # This is kinda redundant
 216        data = payload
 217
 218        # This is always outside the details.
 219        current_user_map: typing.Optional[
 220            collections.Mapping[str, clans.ClanMember]
 221        ] = None
 222        if raw_current_user_map := payload.get("currentUserMemberMap"):
 223            current_user_map = {
 224                membership_type: self.deserialize_clan_member(membership)
 225                for membership_type, membership in raw_current_user_map.items()
 226            }
 227
 228        try:
 229            data = payload["detail"]
 230        except KeyError:
 231            pass
 232
 233        id = data["groupId"]
 234        name = data["name"]
 235        created_at = data["creationDate"]
 236        member_count = data["memberCount"]
 237        about = data["about"]
 238        motto = data["motto"]
 239        is_public = data["isPublic"]
 240        banner = assets.Image(str(data["bannerPath"]))
 241        avatar = assets.Image(str(data["avatarPath"]))
 242        tags = data["tags"]
 243        type = data["groupType"]
 244
 245        features = data["features"]
 246        features_obj = clans.ClanFeatures(
 247            max_members=features["maximumMembers"],
 248            max_membership_types=features["maximumMembershipsOfGroupType"],
 249            capabilities=features["capabilities"],
 250            membership_types=features["membershipTypes"],
 251            invite_permissions=features["invitePermissionOverride"],
 252            update_banner_permissions=features["updateBannerPermissionOverride"],
 253            update_culture_permissions=features["updateCulturePermissionOverride"],
 254            join_level=features["joinLevel"],
 255        )
 256
 257        information: typedefs.JSONObject = data["clanInfo"]
 258        progression: collections.Mapping[int, progressions.Progression] = {
 259            int(prog_hash): self.deserialize_progressions(prog)
 260            for prog_hash, prog in information["d2ClanProgressions"].items()
 261        }
 262
 263        founder: typedefs.NoneOr[clans.ClanMember] = None
 264        if raw_founder := payload.get("founder"):
 265            founder = self.deserialize_clan_member(raw_founder)
 266
 267        return clans.Clan(
 268            net=self._net,
 269            id=int(id),
 270            name=name,
 271            type=enums.GroupType(type),
 272            created_at=time.clean_date(created_at),
 273            member_count=member_count,
 274            motto=motto,
 275            about=about,
 276            is_public=is_public,
 277            banner=banner,
 278            avatar=avatar,
 279            tags=tags,
 280            features=features_obj,
 281            owner=founder,
 282            progressions=progression,
 283            call_sign=information["clanCallsign"],
 284            banner_data=information["clanBannerData"],
 285            chat_security=data["chatSecurity"],
 286            conversation_id=int(data["conversationId"]),
 287            allow_chat=data["allowChat"],
 288            theme=data["theme"],
 289            current_user_membership=current_user_map,
 290        )
 291
 292    def deserialize_clan_member(self, data: typedefs.JSONObject, /) -> clans.ClanMember:
 293        destiny_user = self.deserialize_destiny_membership(data["destinyUserInfo"])
 294        return clans.ClanMember(
 295            net=self._net,
 296            last_seen_name=destiny_user.last_seen_name,
 297            id=destiny_user.id,
 298            name=destiny_user.name,
 299            icon=destiny_user.icon,
 300            last_online=time.from_timestamp(int(data["lastOnlineStatusChange"])),
 301            group_id=int(data["groupId"]),
 302            joined_at=time.clean_date(data["joinDate"]),
 303            types=destiny_user.types,
 304            is_public=destiny_user.is_public,
 305            type=destiny_user.type,
 306            code=destiny_user.code,
 307            is_online=data["isOnline"],
 308            crossave_override=destiny_user.crossave_override,
 309            bungie=self.deserialize_partial_bungie_user(data["bungieNetUserInfo"])
 310            if "bungieNetUserInfo" in data
 311            else None,
 312            member_type=enums.ClanMemberType(int(data["memberType"])),
 313        )
 314
 315    def deserialize_clan_members(
 316        self, data: typedefs.JSONObject, /
 317    ) -> iterators.Iterator[clans.ClanMember]:
 318        return iterators.Iterator(
 319            [self.deserialize_clan_member(member) for member in data["results"]]
 320        )
 321
 322    def deserialize_group_member(
 323        self, payload: typedefs.JSONObject
 324    ) -> clans.GroupMember:
 325        member = payload["member"]
 326        return clans.GroupMember(
 327            net=self._net,
 328            join_date=time.clean_date(member["joinDate"]),
 329            group_id=int(member["groupId"]),
 330            member_type=enums.ClanMemberType(member["memberType"]),
 331            is_online=member["isOnline"],
 332            last_online=time.from_timestamp(int(member["lastOnlineStatusChange"])),
 333            inactive_memberships=payload.get("areAllMembershipsInactive", None),
 334            member=self.deserialize_destiny_membership(member["destinyUserInfo"]),
 335            group=self.deserialize_clan(payload["group"]),
 336        )
 337
 338    def _deserialize_clan_conversation(
 339        self, payload: typedefs.JSONObject
 340    ) -> clans.ClanConversation:
 341        return clans.ClanConversation(
 342            net=self._net,
 343            id=int(payload["conversationId"]),
 344            group_id=int(payload["groupId"]),
 345            name=(
 346                payload["chatName"]
 347                if not typedefs.is_unknown(payload["chatName"])
 348                else undefined.UNDEFINED
 349            ),
 350            chat_enabled=payload["chatEnabled"],
 351            security=payload["chatSecurity"],
 352        )
 353
 354    def deserialize_clan_conversations(
 355        self, payload: typedefs.JSONArray
 356    ) -> collections.Sequence[clans.ClanConversation]:
 357        return [self._deserialize_clan_conversation(conv) for conv in payload]
 358
 359    def deserialize_app_owner(
 360        self, payload: typedefs.JSONObject
 361    ) -> application.ApplicationOwner:
 362        return application.ApplicationOwner(
 363            net=self._net,
 364            name=payload.get("bungieGlobalDisplayName", undefined.UNDEFINED),
 365            id=int(payload["membershipId"]),
 366            type=enums.MembershipType(payload["membershipType"]),
 367            icon=assets.Image(str(payload["iconPath"])),
 368            is_public=payload["isPublic"],
 369            code=payload.get("bungieGlobalDisplayNameCode", None),
 370        )
 371
 372    def deserialize_app(self, payload: typedefs.JSONObject) -> application.Application:
 373        return application.Application(
 374            id=int(payload["applicationId"]),
 375            name=payload["name"],
 376            link=payload["link"],
 377            status=payload["status"],
 378            redirect_url=payload.get("redirectUrl", None),
 379            created_at=time.clean_date(str(payload["creationDate"])),
 380            published_at=time.clean_date(str(payload["firstPublished"])),
 381            owner=self.deserialize_app_owner(payload["team"][0]["user"]),  # type: ignore
 382            scope=payload.get("scope", undefined.UNDEFINED),
 383        )
 384
 385    def _set_character_attrs(self, payload: typedefs.JSONObject) -> character.Character:
 386        total_time = time.format_played(int(payload["minutesPlayedTotal"]), suffix=True)
 387        return character.Character(
 388            net=self._net,
 389            id=int(payload["characterId"]),
 390            gender=enums.Gender(payload["genderType"]),
 391            race=enums.Race(payload["raceType"]),
 392            class_type=enums.Class(payload["classType"]),
 393            emblem=assets.Image(str(payload["emblemBackgroundPath"])),
 394            emblem_icon=assets.Image(str(payload["emblemPath"])),
 395            emblem_hash=int(payload["emblemHash"]),
 396            last_played=time.clean_date(payload["dateLastPlayed"]),
 397            total_played_time=total_time,
 398            member_id=int(payload["membershipId"]),
 399            member_type=enums.MembershipType(payload["membershipType"]),
 400            level=payload["baseCharacterLevel"],
 401            title_hash=payload.get("titleRecordHash", None),
 402            light=payload["light"],
 403            stats={enums.Stat(int(k)): v for k, v in payload["stats"].items()},
 404        )
 405
 406    def deserialize_profile(
 407        self, payload: typedefs.JSONObject, /
 408    ) -> typing.Optional[profile.Profile]:
 409        if (raw_profile := payload.get("data")) is None:
 410            return None
 411
 412        payload = raw_profile
 413        id = int(payload["userInfo"]["membershipId"])
 414        name = payload["userInfo"]["displayName"]
 415        is_public = payload["userInfo"]["isPublic"]
 416        type = enums.MembershipType(payload["userInfo"]["membershipType"])
 417        last_played = time.clean_date(str(payload["dateLastPlayed"]))
 418        character_ids = [int(cid) for cid in payload["characterIds"]]
 419        power_cap = payload["currentSeasonRewardPowerCap"]
 420
 421        return profile.Profile(
 422            id=int(id),
 423            name=name,
 424            is_public=is_public,
 425            type=type,
 426            last_played=last_played,
 427            character_ids=character_ids,
 428            power_cap=power_cap,
 429            net=self._net,
 430        )
 431
 432    def deserialize_profile_item(
 433        self, payload: typedefs.JSONObject
 434    ) -> profile.ProfileItemImpl:
 435
 436        instance_id: typing.Optional[int] = None
 437        if raw_instance_id := payload.get("itemInstanceId"):
 438            instance_id = int(raw_instance_id)
 439
 440        version_number: typing.Optional[int] = None
 441        if raw_version := payload.get("versionNumber"):
 442            version_number = int(raw_version)
 443
 444        transfer_status = enums.TransferStatus(payload["transferStatus"])
 445
 446        return profile.ProfileItemImpl(
 447            net=self._net,
 448            hash=payload["itemHash"],
 449            quantity=payload["quantity"],
 450            bind_status=enums.ItemBindStatus(payload["bindStatus"]),
 451            location=enums.ItemLocation(payload["location"]),
 452            bucket=payload["bucketHash"],
 453            transfer_status=transfer_status,
 454            lockable=payload["lockable"],
 455            state=enums.ItemState(payload["state"]),
 456            dismantel_permissions=payload["dismantlePermission"],
 457            is_wrapper=payload["isWrapper"],
 458            instance_id=instance_id,
 459            version_number=version_number,
 460            ornament_id=payload.get("overrideStyleItemHash"),
 461        )
 462
 463    def deserialize_objectives(self, payload: typedefs.JSONObject) -> records.Objective:
 464        return records.Objective(
 465            net=self._net,
 466            hash=payload["objectiveHash"],
 467            visible=payload["visible"],
 468            complete=payload["complete"],
 469            completion_value=payload["completionValue"],
 470            progress=payload.get("progress"),
 471            destination_hash=payload.get("destinationHash"),
 472            activity_hash=payload.get("activityHash"),
 473        )
 474
 475    def deserialize_records(
 476        self,
 477        payload: typedefs.JSONObject,
 478        scores: typing.Optional[records.RecordScores] = None,
 479        **nodes: int,
 480    ) -> records.Record:
 481        objectives: typing.Optional[list[records.Objective]] = None
 482        interval_objectives: typing.Optional[list[records.Objective]] = None
 483        record_state: typedefs.IntAnd[records.RecordState]
 484
 485        record_state = records.RecordState(payload["state"])
 486
 487        if raw_objs := payload.get("objectives"):
 488            objectives = [self.deserialize_objectives(obj) for obj in raw_objs]
 489
 490        if raw_interval_objs := payload.get("intervalObjectives"):
 491            interval_objectives = [
 492                self.deserialize_objectives(obj) for obj in raw_interval_objs
 493            ]
 494
 495        return records.Record(
 496            scores=scores,
 497            categories_node_hash=nodes.get("categories_hash", undefined.UNDEFINED),
 498            seals_node_hash=nodes.get("seals_hash", undefined.UNDEFINED),
 499            state=record_state,
 500            objectives=objectives,
 501            interval_objectives=interval_objectives,
 502            redeemed_count=payload.get("intervalsRedeemedCount", 0),
 503            completion_times=payload.get("completedCount", None),
 504            reward_visibility=payload.get("rewardVisibilty", None),
 505        )
 506
 507    def deserialize_character_records(
 508        self,
 509        payload: typedefs.JSONObject,
 510        scores: typing.Optional[records.RecordScores] = None,
 511        record_hashes: typing.Optional[list[int]] = None,
 512    ) -> records.CharacterRecord:
 513
 514        record = self.deserialize_records(payload, scores)
 515        return records.CharacterRecord(
 516            scores=scores,
 517            categories_node_hash=record.categories_node_hash,
 518            seals_node_hash=record.seals_node_hash,
 519            state=record.state,
 520            objectives=record.objectives,
 521            interval_objectives=record.interval_objectives,
 522            redeemed_count=payload.get("intervalsRedeemedCount", 0),
 523            completion_times=payload.get("completedCount"),
 524            reward_visibility=payload.get("rewardVisibilty"),
 525            record_hashes=record_hashes or [],
 526        )
 527
 528    def deserialize_character_dye(self, payload: typedefs.JSONObject) -> character.Dye:
 529        return character.Dye(
 530            channel_hash=payload["channelHash"], dye_hash=payload["dyeHash"]
 531        )
 532
 533    def deserialize_character_customization(
 534        self, payload: typedefs.JSONObject
 535    ) -> character.CustomizationOptions:
 536        return character.CustomizationOptions(
 537            personality=payload["personality"],
 538            face=payload["face"],
 539            skin_color=payload["skinColor"],
 540            lip_color=payload["lipColor"],
 541            eye_color=payload["eyeColor"],
 542            hair_colors=payload.get("hairColors", []),
 543            feature_colors=payload.get("featureColors", []),
 544            decal_color=payload["decalColor"],
 545            wear_helmet=payload["wearHelmet"],
 546            hair_index=payload["hairIndex"],
 547            feature_index=payload["featureIndex"],
 548            decal_index=payload["decalIndex"],
 549        )
 550
 551    def deserialize_character_minimal_equipments(
 552        self, payload: typedefs.JSONObject
 553    ) -> character.MinimalEquipments:
 554        dyes = None
 555        if raw_dyes := payload.get("dyes"):
 556            if raw_dyes:
 557                dyes = [self.deserialize_character_dye(dye) for dye in raw_dyes]
 558        return character.MinimalEquipments(
 559            net=self._net, item_hash=payload["itemHash"], dyes=dyes
 560        )
 561
 562    def deserialize_character_render_data(
 563        self, payload: typedefs.JSONObject, /
 564    ) -> character.RenderedData:
 565        return character.RenderedData(
 566            net=self._net,
 567            customization=self.deserialize_character_customization(
 568                payload["customization"]
 569            ),
 570            custom_dyes=[
 571                self.deserialize_character_dye(dye)
 572                for dye in payload["customDyes"]
 573                if dye
 574            ],
 575            equipment=[
 576                self.deserialize_character_minimal_equipments(equipment)
 577                for equipment in payload["peerView"]["equipment"]
 578            ],
 579        )
 580
 581    def deserialize_available_activity(
 582        self, payload: typedefs.JSONObject
 583    ) -> activity.AvailableActivity:
 584        return activity.AvailableActivity(
 585            hash=payload["activityHash"],
 586            is_new=payload["isNew"],
 587            is_completed=payload["isCompleted"],
 588            is_visible=payload["isVisible"],
 589            display_level=payload.get("displayLevel"),
 590            recommended_light=payload.get("recommendedLight"),
 591            difficulty=activity.Difficulty(payload["difficultyTier"]),
 592            can_join=payload["canJoin"],
 593            can_lead=payload["canLead"],
 594        )
 595
 596    def deserialize_character_activity(
 597        self, payload: typedefs.JSONObject
 598    ) -> activity.CharacterActivity:
 599        current_mode: typing.Optional[enums.GameMode] = None
 600        if raw_current_mode := payload.get("currentActivityModeType"):
 601            current_mode = enums.GameMode(raw_current_mode)
 602
 603        current_mode_types: typing.Optional[collections.Sequence[enums.GameMode]] = None
 604        if raw_current_modes := payload.get("currentActivityModeTypes"):
 605            current_mode_types = [enums.GameMode(type_) for type_ in raw_current_modes]
 606
 607        return activity.CharacterActivity(
 608            date_started=time.clean_date(payload["dateActivityStarted"]),
 609            current_hash=payload["currentActivityHash"],
 610            current_mode_hash=payload["currentActivityModeHash"],
 611            current_mode=current_mode,
 612            current_mode_hashes=payload.get("currentActivityModeHashes"),
 613            current_mode_types=current_mode_types,
 614            current_playlist_hash=payload.get("currentPlaylistActivityHash"),
 615            last_story_hash=payload["lastCompletedStoryHash"],
 616            available_activities=[
 617                self.deserialize_available_activity(activity_)
 618                for activity_ in payload["availableActivities"]
 619            ],
 620        )
 621
 622    def deserialize_profile_items(
 623        self, payload: typedefs.JSONObject, /
 624    ) -> list[profile.ProfileItemImpl]:
 625        return [self.deserialize_profile_item(item) for item in payload["items"]]
 626
 627    def _deserialize_node(self, payload: typedefs.JSONObject) -> records.Node:
 628        return records.Node(
 629            state=int(payload["state"]),
 630            objective=self.deserialize_objectives(payload["objective"])
 631            if "objective" in payload
 632            else None,
 633            progress_value=int(payload["progressValue"]),
 634            completion_value=int(payload["completionValue"]),
 635            record_category_score=int(payload["recordCategoryScore"])
 636            if "recordCategoryScore" in payload
 637            else None,
 638        )
 639
 640    @staticmethod
 641    def _deserialize_collectible(payload: typedefs.JSONObject) -> items.Collectible:
 642        recent_collectibles: typing.Optional[collections.Collection[int]] = None
 643        if raw_recent_collectibles := payload.get("recentCollectibleHashes"):
 644            recent_collectibles = [
 645                int(item_hash) for item_hash in raw_recent_collectibles
 646            ]
 647
 648        collectibles: dict[int, int] = {}
 649        for item_hash, mapping in payload["collectibles"].items():
 650            collectibles[int(item_hash)] = int(mapping["state"])
 651
 652        return items.Collectible(
 653            recent_collectibles=recent_collectibles,
 654            collectibles=collectibles,
 655            collection_categorie_hash=int(payload["collectionCategoriesRootNodeHash"]),
 656            collection_badges_hash=int(payload["collectionBadgesRootNodeHash"]),
 657        )
 658
 659    @staticmethod
 660    def _deserialize_currencies(
 661        payload: typedefs.JSONObject,
 662    ) -> collections.Sequence[items.Currency]:
 663        return [
 664            items.Currency(hash=int(item_hash), amount=int(amount))
 665            for item_hash, amount in payload["itemQuantities"].items()
 666        ]
 667
 668    def deserialize_progressions(
 669        self, payload: typedefs.JSONObject
 670    ) -> progressions.Progression:
 671        return progressions.Progression(
 672            hash=int(payload["progressionHash"]),
 673            level=int(payload["level"]),
 674            cap=int(payload["levelCap"]),
 675            daily_limit=int(payload["dailyLimit"]),
 676            weekly_limit=int(payload["weeklyLimit"]),
 677            current_progress=int(payload["currentProgress"]),
 678            daily_progress=int(payload["dailyProgress"]),
 679            needed=int(payload["progressToNextLevel"]),
 680            next_level=int(payload["nextLevelAt"]),
 681        )
 682
 683    def _deserialize_factions(
 684        self, payload: typedefs.JSONObject
 685    ) -> progressions.Factions:
 686        progs = self.deserialize_progressions(payload)
 687        return progressions.Factions(
 688            hash=progs.hash,
 689            level=progs.level,
 690            cap=progs.cap,
 691            daily_limit=progs.daily_limit,
 692            weekly_limit=progs.weekly_limit,
 693            current_progress=progs.current_progress,
 694            daily_progress=progs.daily_progress,
 695            needed=progs.needed,
 696            next_level=progs.next_level,
 697            faction_hash=payload["factionHash"],
 698            faction_vendor_hash=payload["factionVendorIndex"],
 699        )
 700
 701    def _deserialize_milestone_available_quest(
 702        self, payload: typedefs.JSONObject
 703    ) -> milestones.MilestoneQuest:
 704        return milestones.MilestoneQuest(
 705            item_hash=payload["questItemHash"],
 706            status=self._deserialize_milestone_quest_status(payload["status"]),
 707        )
 708
 709    def _deserialize_milestone_activity(
 710        self, payload: typedefs.JSONObject
 711    ) -> milestones.MilestoneActivity:
 712
 713        phases: typing.Optional[
 714            collections.Sequence[milestones.MilestoneActivityPhase]
 715        ] = None
 716        if raw_phases := payload.get("phases"):
 717            phases = [
 718                milestones.MilestoneActivityPhase(
 719                    is_completed=obj["complete"], hash=obj["phaseHash"]
 720                )
 721                for obj in raw_phases
 722            ]
 723
 724        return milestones.MilestoneActivity(
 725            hash=payload["activityHash"],
 726            challenges=[
 727                self.deserialize_objectives(obj["objective"])
 728                for obj in payload["challenges"]
 729            ],
 730            modifier_hashes=payload.get("modifierHashes"),
 731            boolean_options=payload.get("booleanActivityOptions"),
 732            phases=phases,
 733        )
 734
 735    def _deserialize_milestone_quest_status(
 736        self, payload: typedefs.JSONObject
 737    ) -> milestones.QuestStatus:
 738        return milestones.QuestStatus(
 739            net=self._net,
 740            quest_hash=payload["questHash"],
 741            step_hash=payload["stepHash"],
 742            step_objectives=[
 743                self.deserialize_objectives(objective)
 744                for objective in payload["stepObjectives"]
 745            ],
 746            is_tracked=payload["tracked"],
 747            is_completed=payload["completed"],
 748            started=payload["started"],
 749            item_instance_id=payload["itemInstanceId"],
 750            vendor_hash=payload.get("vendorHash"),
 751            is_redeemed=payload["redeemed"],
 752        )
 753
 754    def _deserialize_milestone_rewards(
 755        self, payload: typedefs.JSONObject
 756    ) -> milestones.MilestoneReward:
 757        return milestones.MilestoneReward(
 758            category_hash=payload["rewardCategoryHash"],
 759            entries=[
 760                milestones.MilestoneRewardEntry(
 761                    entry_hash=entry["rewardEntryHash"],
 762                    is_earned=entry["earned"],
 763                    is_redeemed=entry["redeemed"],
 764                )
 765                for entry in payload["entries"]
 766            ],
 767        )
 768
 769    def deserialize_milestone(
 770        self, payload: typedefs.JSONObject
 771    ) -> milestones.Milestone:
 772        start_date: typing.Optional[datetime.datetime] = None
 773        if raw_start_date := payload.get("startDate"):
 774            start_date = time.clean_date(raw_start_date)
 775
 776        end_date: typing.Optional[datetime.datetime] = None
 777        if raw_end_date := payload.get("endDate"):
 778            end_date = time.clean_date(raw_end_date)
 779
 780        rewards: typing.Optional[
 781            collections.Collection[milestones.MilestoneReward]
 782        ] = None
 783        if raw_rewards := payload.get("rewards"):
 784            rewards = [
 785                self._deserialize_milestone_rewards(reward) for reward in raw_rewards
 786            ]
 787
 788        activities: typing.Optional[
 789            collections.Sequence[milestones.MilestoneActivity]
 790        ] = None
 791        if raw_activities := payload.get("activities"):
 792            activities = [
 793                self._deserialize_milestone_activity(active)
 794                for active in raw_activities
 795            ]
 796
 797        quests: typing.Optional[collections.Sequence[milestones.MilestoneQuest]] = None
 798        if raw_quests := payload.get("availableQuests"):
 799            quests = [
 800                self._deserialize_milestone_available_quest(quest)
 801                for quest in raw_quests
 802            ]
 803
 804        vendors: typing.Optional[
 805            collections.Sequence[milestones.MilestoneVendor]
 806        ] = None
 807        if raw_vendors := payload.get("vendors"):
 808            vendors = [
 809                milestones.MilestoneVendor(
 810                    vendor_hash=vendor["vendorHash"],
 811                    preview_itemhash=vendor.get("previewItemHash"),
 812                )
 813                for vendor in raw_vendors
 814            ]
 815
 816        return milestones.Milestone(
 817            hash=payload["milestoneHash"],
 818            start_date=start_date,
 819            end_date=end_date,
 820            order=payload["order"],
 821            rewards=rewards,
 822            available_quests=quests,
 823            activities=activities,
 824            vendors=vendors,
 825        )
 826
 827    def _deserialize_artifact_tiers(
 828        self, payload: typedefs.JSONObject
 829    ) -> season.ArtifactTier:
 830        return season.ArtifactTier(
 831            hash=payload["tierHash"],
 832            is_unlocked=payload["isUnlocked"],
 833            points_to_unlock=payload["pointsToUnlock"],
 834            items=[
 835                season.ArtifactTierItem(
 836                    hash=item["itemHash"], is_active=item["isActive"]
 837                )
 838                for item in payload["items"]
 839            ],
 840        )
 841
 842    def deserialize_characters(
 843        self, payload: typedefs.JSONObject
 844    ) -> collections.Mapping[int, character.Character]:
 845        return {
 846            int(char_id): self._set_character_attrs(char)
 847            for char_id, char in payload["data"].items()
 848        }
 849
 850    def deserialize_character(
 851        self, payload: typedefs.JSONObject
 852    ) -> character.Character:
 853        return self._set_character_attrs(payload)
 854
 855    def deserialize_character_equipments(
 856        self, payload: typedefs.JSONObject
 857    ) -> collections.Mapping[int, collections.Sequence[profile.ProfileItemImpl]]:
 858        return {
 859            int(char_id): self.deserialize_profile_items(item)
 860            for char_id, item in payload["data"].items()
 861        }
 862
 863    def deserialize_character_activities(
 864        self, payload: typedefs.JSONObject
 865    ) -> collections.Mapping[int, activity.CharacterActivity]:
 866        return {
 867            int(char_id): self.deserialize_character_activity(data)
 868            for char_id, data in payload["data"].items()
 869        }
 870
 871    def deserialize_characters_render_data(
 872        self, payload: typedefs.JSONObject
 873    ) -> collections.Mapping[int, character.RenderedData]:
 874        return {
 875            int(char_id): self.deserialize_character_render_data(data)
 876            for char_id, data in payload["data"].items()
 877        }
 878
 879    def deserialize_character_progressions(
 880        self, payload: typedefs.JSONObject
 881    ) -> character.CharacterProgression:
 882        progressions_ = {
 883            int(prog_id): self.deserialize_progressions(prog)
 884            for prog_id, prog in payload["progressions"].items()
 885        }
 886
 887        factions = {
 888            int(faction_id): self._deserialize_factions(faction)
 889            for faction_id, faction in payload["factions"].items()
 890        }
 891
 892        milestones_ = {
 893            int(milestone_hash): self.deserialize_milestone(milestone)
 894            for milestone_hash, milestone in payload["milestones"].items()
 895        }
 896
 897        uninstanced_item_objectives = {
 898            int(item_hash): [self.deserialize_objectives(ins) for ins in obj]
 899            for item_hash, obj in payload["uninstancedItemObjectives"].items()
 900        }
 901
 902        artifact = payload["seasonalArtifact"]
 903        seasonal_artifact = season.CharacterScopedArtifact(
 904            hash=artifact["artifactHash"],
 905            points_used=artifact["pointsUsed"],
 906            reset_count=artifact["resetCount"],
 907            tiers=[
 908                self._deserialize_artifact_tiers(tier) for tier in artifact["tiers"]
 909            ],
 910        )
 911        checklists = payload["checklists"]
 912
 913        return character.CharacterProgression(
 914            progressions=progressions_,
 915            factions=factions,
 916            checklists=checklists,
 917            milestones=milestones_,
 918            seasonal_artifact=seasonal_artifact,
 919            uninstanced_item_objectives=uninstanced_item_objectives,
 920        )
 921
 922    def deserialize_character_progressions_mapping(
 923        self, payload: typedefs.JSONObject
 924    ) -> collections.Mapping[int, character.CharacterProgression]:
 925        character_progressions: collections.Mapping[
 926            int, character.CharacterProgression
 927        ] = {}
 928        for char_id, data in payload["data"].items():
 929            # A little hack to stop mypy complaining about Mapping <-> dict
 930            character_progressions[int(char_id)] = self.deserialize_character_progressions(data)  # type: ignore[index]
 931        return character_progressions
 932
 933    def deserialize_characters_records(
 934        self,
 935        payload: typedefs.JSONObject,
 936    ) -> collections.Mapping[int, records.CharacterRecord]:
 937
 938        return {
 939            int(rec_id): self.deserialize_character_records(
 940                rec, record_hashes=payload.get("featuredRecordHashes")
 941            )
 942            for rec_id, rec in payload["records"].items()
 943        }
 944
 945    def deserialize_profile_records(
 946        self, payload: typedefs.JSONObject
 947    ) -> collections.Mapping[int, records.Record]:
 948        raw_profile_records = payload["data"]
 949        scores = records.RecordScores(
 950            current_score=raw_profile_records["score"],
 951            legacy_score=raw_profile_records["legacyScore"],
 952            lifetime_score=raw_profile_records["lifetimeScore"],
 953        )
 954        return {
 955            int(record_id): self.deserialize_records(
 956                record,
 957                scores,
 958                categories_hash=raw_profile_records["recordCategoriesRootNodeHash"],
 959                seals_hash=raw_profile_records["recordSealsRootNodeHash"],
 960            )
 961            for record_id, record in raw_profile_records["records"].items()
 962        }
 963
 964    def _deserialize_craftable_socket_plug(
 965        self, payload: typedefs.JSONObject
 966    ) -> items.CraftableSocketPlug:
 967        return items.CraftableSocketPlug(
 968            item_hash=int(payload["plugItemHash"]),
 969            failed_requirement_indexes=payload.get("failedRequirementIndexes", []),
 970        )
 971
 972    def _deserialize_craftable_socket(
 973        self, payload: typedefs.JSONObject
 974    ) -> items.CraftableSocket:
 975
 976        plugs: list[items.CraftableSocketPlug] = []
 977        if raw_plug := payload.get("plug"):
 978            plugs.extend(
 979                self._deserialize_craftable_socket_plug(plug) for plug in raw_plug
 980            )
 981
 982        return items.CraftableSocket(
 983            plug_set_hash=int(payload["plugSetHash"]), plugs=plugs
 984        )
 985
 986    def _deserialize_craftable_item(
 987        self, payload: typedefs.JSONObject
 988    ) -> items.CraftableItem:
 989
 990        return items.CraftableItem(
 991            is_visible=payload["visible"],
 992            failed_requirement_indexes=payload.get("failedRequirementIndexes", []),
 993            sockets=[
 994                self._deserialize_craftable_socket(socket)
 995                for socket in payload["sockets"]
 996            ],
 997        )
 998
 999    def deserialize_craftables_component(
1000        self, payload: typedefs.JSONObject
1001    ) -> components.CraftablesComponent:
1002        return components.CraftablesComponent(
1003            net=self._net,
1004            craftables={
1005                int(item_id): self._deserialize_craftable_item(item)
1006                for item_id, item in payload["craftables"].items()
1007                if item is not None
1008            },
1009            crafting_root_node_hash=payload["craftingRootNodeHash"],
1010        )
1011
1012    def deserialize_components(  # noqa: C901 Too complex.
1013        self, payload: typedefs.JSONObject
1014    ) -> components.Component:
1015
1016        profile_: typing.Optional[profile.Profile] = None
1017        if raw_profile := payload.get("profile"):
1018            profile_ = self.deserialize_profile(raw_profile)
1019
1020        profile_progression: typing.Optional[profile.ProfileProgression] = None
1021        if raw_profile_progression := payload.get("profileProgression"):
1022            profile_progression = self.deserialize_profile_progression(
1023                raw_profile_progression
1024            )
1025
1026        profile_currencies: typing.Optional[
1027            collections.Sequence[profile.ProfileItemImpl]
1028        ] = None
1029        if raw_profile_currencies := payload.get("profileCurrencies"):
1030            if "data" in raw_profile_currencies:
1031                profile_currencies = self.deserialize_profile_items(
1032                    raw_profile_currencies["data"]
1033                )
1034
1035        profile_inventories: typing.Optional[
1036            collections.Sequence[profile.ProfileItemImpl]
1037        ] = None
1038        if raw_profile_inventories := payload.get("profileInventory"):
1039            if "data" in raw_profile_inventories:
1040                profile_inventories = self.deserialize_profile_items(
1041                    raw_profile_inventories["data"]
1042                )
1043
1044        profile_records: typing.Optional[
1045            collections.Mapping[int, records.Record]
1046        ] = None
1047
1048        if raw_profile_records_ := payload.get("profileRecords"):
1049            profile_records = self.deserialize_profile_records(raw_profile_records_)
1050
1051        characters: typing.Optional[typing.Mapping[int, character.Character]] = None
1052        if raw_characters := payload.get("characters"):
1053            characters = self.deserialize_characters(raw_characters)
1054
1055        character_records: typing.Optional[
1056            collections.Mapping[int, records.CharacterRecord]
1057        ] = None
1058
1059        if raw_character_records := payload.get("characterRecords"):
1060            # Had to do it in two steps..
1061            to_update: typedefs.JSONObject = {}
1062            for _, data in raw_character_records["data"].items():
1063                for record_id, record in data.items():
1064                    to_update[record_id] = record
1065
1066            character_records = {
1067                int(rec_id): self.deserialize_character_records(
1068                    rec, record_hashes=to_update.get("featuredRecordHashes")
1069                )
1070                for rec_id, rec in to_update["records"].items()
1071            }
1072
1073        character_equipments: typing.Optional[
1074            collections.Mapping[int, collections.Sequence[profile.ProfileItemImpl]]
1075        ] = None
1076        if raw_character_equips := payload.get("characterEquipment"):
1077            character_equipments = self.deserialize_character_equipments(
1078                raw_character_equips
1079            )
1080
1081        character_inventories: typing.Optional[
1082            collections.Mapping[int, collections.Sequence[profile.ProfileItemImpl]]
1083        ] = None
1084        if raw_character_inventories := payload.get("characterInventories"):
1085            if "data" in raw_character_inventories:
1086                character_inventories = self.deserialize_character_equipments(
1087                    raw_character_inventories
1088                )
1089
1090        character_activities: typing.Optional[
1091            collections.Mapping[int, activity.CharacterActivity]
1092        ] = None
1093        if raw_char_acts := payload.get("characterActivities"):
1094            character_activities = self.deserialize_character_activities(raw_char_acts)
1095
1096        character_render_data: typing.Optional[
1097            collections.Mapping[int, character.RenderedData]
1098        ] = None
1099        if raw_character_render_data := payload.get("characterRenderData"):
1100            character_render_data = self.deserialize_characters_render_data(
1101                raw_character_render_data
1102            )
1103
1104        character_progressions: typing.Optional[
1105            collections.Mapping[int, character.CharacterProgression]
1106        ] = None
1107
1108        if raw_character_progressions := payload.get("characterProgressions"):
1109            character_progressions = self.deserialize_character_progressions_mapping(
1110                raw_character_progressions
1111            )
1112
1113        profile_string_vars: typing.Optional[collections.Mapping[int, int]] = None
1114        if raw_profile_string_vars := payload.get("profileStringVariables"):
1115            profile_string_vars = raw_profile_string_vars["data"]["integerValuesByHash"]
1116
1117        character_string_vars: typing.Optional[
1118            collections.Mapping[int, collections.Mapping[int, int]]
1119        ] = None
1120        if raw_character_string_vars := payload.get("characterStringVariables"):
1121            character_string_vars = {
1122                int(char_id): data["integerValuesByHash"]
1123                for char_id, data in raw_character_string_vars["data"].items()
1124            }
1125
1126        metrics: typing.Optional[
1127            collections.Sequence[
1128                collections.Mapping[
1129                    int, tuple[bool, typing.Optional[records.Objective]]
1130                ]
1131            ]
1132        ] = None
1133        root_node_hash: typing.Optional[int] = None
1134
1135        if raw_metrics := payload.get("metrics"):
1136            root_node_hash = raw_metrics["data"]["metricsRootNodeHash"]
1137            metrics = [
1138                {
1139                    int(metrics_hash): (
1140                        data["invisible"],
1141                        self.deserialize_objectives(data["objectiveProgress"])
1142                        if "objectiveProgress" in data
1143                        else None,
1144                    )
1145                    for metrics_hash, data in raw_metrics["data"]["metrics"].items()
1146                }
1147            ]
1148        transitory: typing.Optional[fireteams.FireteamParty] = None
1149        if raw_transitory := payload.get("profileTransitoryData"):
1150            if "data" in raw_transitory:
1151                transitory = self.deserialize_fireteam_party(raw_transitory["data"])
1152
1153        item_components: typing.Optional[components.ItemsComponent] = None
1154        if raw_item_components := payload.get("itemComponents"):
1155            item_components = self.deserialize_items_component(raw_item_components)
1156
1157        profile_plugsets: typing.Optional[
1158            collections.Mapping[int, collections.Sequence[items.PlugItemState]]
1159        ] = None
1160
1161        if raw_profile_plugs := payload.get("profilePlugSets"):
1162            profile_plugsets = {
1163                int(index): [self.deserialize_plug_item_state(state) for state in data]
1164                for index, data in raw_profile_plugs["data"]["plugs"].items()
1165            }
1166
1167        character_plugsets: typing.Optional[
1168            collections.Mapping[
1169                int, collections.Mapping[int, collections.Sequence[items.PlugItemState]]
1170            ]
1171        ] = None
1172        if raw_char_plugsets := payload.get("characterPlugSets"):
1173            character_plugsets = {
1174                int(char_id): {
1175                    int(index): [
1176                        self.deserialize_plug_item_state(state) for state in data
1177                    ]
1178                    for index, data in inner["plugs"].items()
1179                }
1180                for char_id, inner in raw_char_plugsets["data"].items()
1181            }
1182
1183        character_collectibles: typing.Optional[
1184            collections.Mapping[int, items.Collectible]
1185        ] = None
1186        if raw_character_collectibles := payload.get("characterCollectibles"):
1187            character_collectibles = {
1188                int(char_id): self._deserialize_collectible(data)
1189                for char_id, data in raw_character_collectibles["data"].items()
1190            }
1191
1192        profile_collectibles: typing.Optional[items.Collectible] = None
1193        if raw_profile_collectibles := payload.get("profileCollectibles"):
1194            profile_collectibles = self._deserialize_collectible(
1195                raw_profile_collectibles["data"]
1196            )
1197
1198        profile_nodes: typing.Optional[collections.Mapping[int, records.Node]] = None
1199        if raw_profile_nodes := payload.get("profilePresentationNodes"):
1200            profile_nodes = {
1201                int(node_hash): self._deserialize_node(node)
1202                for node_hash, node in raw_profile_nodes["data"]["nodes"].items()
1203            }
1204
1205        character_nodes: typing.Optional[
1206            collections.Mapping[int, collections.Mapping[int, records.Node]]
1207        ] = None
1208        if raw_character_nodes := payload.get("characterPresentationNodes"):
1209            character_nodes = {
1210                int(char_id): {
1211                    int(node_hash): self._deserialize_node(node)
1212                    for node_hash, node in each_character["nodes"].items()
1213                }
1214                for char_id, each_character in raw_character_nodes["data"].items()
1215            }
1216
1217        platform_silver: typing.Optional[
1218            collections.Mapping[str, profile.ProfileItemImpl]
1219        ] = None
1220        if raw_platform_silver := payload.get("platformSilver"):
1221            if "data" in raw_platform_silver:
1222                platform_silver = {
1223                    platform_name: self.deserialize_profile_item(item)
1224                    for platform_name, item in raw_platform_silver["data"][
1225                        "platformSilver"
1226                    ].items()
1227                }
1228
1229        character_currency_lookups: typing.Optional[
1230            collections.Mapping[int, collections.Sequence[items.Currency]]
1231        ] = None
1232        if raw_char_lookups := payload.get("characterCurrencyLookups"):
1233            if "data" in raw_char_lookups:
1234                character_currency_lookups = {
1235                    int(char_id): self._deserialize_currencies(currencie)
1236                    for char_id, currencie in raw_char_lookups["data"].items()
1237                }
1238
1239        character_craftables: typing.Optional[
1240            collections.Mapping[int, components.CraftablesComponent]
1241        ] = None
1242        if raw_character_craftables := payload.get("characterCraftables"):
1243
1244            if "data" in raw_character_craftables:
1245                character_craftables = {
1246                    int(char_id): self.deserialize_craftables_component(craftable)
1247                    for char_id, craftable in raw_character_craftables["data"].items()
1248                }
1249
1250        return components.Component(
1251            profiles=profile_,
1252            profile_progression=profile_progression,
1253            profile_currencies=profile_currencies,
1254            profile_inventories=profile_inventories,
1255            profile_records=profile_records,
1256            characters=characters,
1257            character_records=character_records,
1258            character_equipments=character_equipments,
1259            character_inventories=character_inventories,
1260            character_activities=character_activities,
1261            character_render_data=character_render_data,
1262            character_progressions=character_progressions,
1263            profile_string_variables=profile_string_vars,
1264            character_string_variables=character_string_vars,
1265            metrics=metrics,
1266            root_node_hash=root_node_hash,
1267            transitory=transitory,
1268            item_components=item_components,
1269            profile_plugsets=profile_plugsets,
1270            character_plugsets=character_plugsets,
1271            character_collectibles=character_collectibles,
1272            profile_collectibles=profile_collectibles,
1273            profile_nodes=profile_nodes,
1274            character_nodes=character_nodes,
1275            platform_silver=platform_silver,
1276            character_currency_lookups=character_currency_lookups,
1277            character_craftables=character_craftables,
1278        )
1279
1280    def deserialize_items_component(
1281        self, payload: typedefs.JSONObject
1282    ) -> components.ItemsComponent:
1283        instances: typing.Optional[
1284            collections.Sequence[collections.Mapping[int, items.ItemInstance]]
1285        ] = None
1286        if raw_instances := payload.get("instances"):
1287            instances = [
1288                {
1289                    int(ins_id): self.deserialize_instanced_item(item)
1290                    for ins_id, item in raw_instances["data"].items()
1291                }
1292            ]
1293
1294        render_data: typing.Optional[
1295            collections.Mapping[int, tuple[bool, dict[int, int]]]
1296        ] = None
1297        if raw_render_data := payload.get("renderData"):
1298            render_data = {
1299                int(ins_id): (data["useCustomDyes"], data["artRegions"])
1300                for ins_id, data in raw_render_data["data"].items()
1301            }
1302
1303        stats: typing.Optional[collections.Mapping[int, items.ItemStatsView]] = None
1304        if raw_stats := payload.get("stats"):
1305            builder: collections.Mapping[int, items.ItemStatsView] = {}
1306            for ins_id, stat in raw_stats["data"].items():
1307                for _, items_ in stat.items():
1308                    builder[int(ins_id)] = self.deserialize_item_stats_view(items_)  # type: ignore[index]
1309            stats = builder
1310
1311        sockets: typing.Optional[
1312            collections.Mapping[int, collections.Sequence[items.ItemSocket]]
1313        ] = None
1314        if raw_sockets := payload.get("sockets"):
1315            sockets = {
1316                int(ins_id): [
1317                    self.deserialize_item_socket(socket) for socket in item["sockets"]
1318                ]
1319                for ins_id, item in raw_sockets["data"].items()
1320            }
1321
1322        objeectives: typing.Optional[
1323            collections.Mapping[int, collections.Sequence[records.Objective]]
1324        ] = None
1325        if raw_objectives := payload.get("objectives"):
1326            objeectives = {
1327                int(ins_id): [self.deserialize_objectives(objective)]
1328                for ins_id, data in raw_objectives["data"].items()
1329                for objective in data["objectives"]
1330            }
1331
1332        perks: typing.Optional[
1333            collections.Mapping[int, collections.Collection[items.ItemPerk]]
1334        ] = None
1335        if raw_perks := payload.get("perks"):
1336            perks = {
1337                int(ins_id): [
1338                    self.deserialize_item_perk(perk) for perk in item["perks"]
1339                ]
1340                for ins_id, item in raw_perks["data"].items()
1341            }
1342
1343        plug_states: typing.Optional[collections.Sequence[items.PlugItemState]] = None
1344        if raw_plug_states := payload.get("plugStates"):
1345            pending_states: list[items.PlugItemState] = []
1346            for _, plug in raw_plug_states["data"].items():
1347                pending_states.append(self.deserialize_plug_item_state(plug))
1348            plug_states = pending_states
1349
1350        reusable_plugs: typing.Optional[
1351            collections.Mapping[int, collections.Sequence[items.PlugItemState]]
1352        ] = None
1353        if raw_re_plugs := payload.get("reusablePlugs"):
1354            reusable_plugs = {
1355                int(ins_id): [
1356                    self.deserialize_plug_item_state(state) for state in inner
1357                ]
1358                for ins_id, plug in raw_re_plugs["data"].items()
1359                for inner in list(plug["plugs"].values())
1360            }
1361
1362        plug_objectives: typing.Optional[
1363            collections.Mapping[
1364                int, collections.Mapping[int, collections.Collection[records.Objective]]
1365            ]
1366        ] = None
1367        if raw_plug_objectives := payload.get("plugObjectives"):
1368            plug_objectives = {
1369                int(ins_id): {
1370                    int(obj_hash): [self.deserialize_objectives(obj) for obj in objs]
1371                    for obj_hash, objs in inner["objectivesPerPlug"].items()
1372                }
1373                for ins_id, inner in raw_plug_objectives["data"].items()
1374            }
1375
1376        return components.ItemsComponent(
1377            sockets=sockets,
1378            stats=stats,
1379            render_data=render_data,
1380            instances=instances,
1381            objectives=objeectives,
1382            perks=perks,
1383            plug_states=plug_states,
1384            reusable_plugs=reusable_plugs,
1385            plug_objectives=plug_objectives,
1386        )
1387
1388    def deserialize_character_component(  # type: ignore[call-arg]
1389        self, payload: typedefs.JSONObject
1390    ) -> components.CharacterComponent:
1391
1392        character_: typing.Optional[character.Character] = None
1393        if raw_singuler_character := payload.get("character"):
1394            character_ = self.deserialize_character(raw_singuler_character["data"])
1395
1396        inventory: typing.Optional[collections.Sequence[profile.ProfileItemImpl]] = None
1397        if raw_inventory := payload.get("inventory"):
1398            if "data" in raw_inventory:
1399                inventory = self.deserialize_profile_items(raw_inventory["data"])
1400
1401        activities: typing.Optional[activity.CharacterActivity] = None
1402        if raw_activities := payload.get("activities"):
1403            activities = self.deserialize_character_activity(raw_activities["data"])
1404
1405        equipment: typing.Optional[collections.Sequence[profile.ProfileItemImpl]] = None
1406        if raw_equipments := payload.get("equipment"):
1407            equipment = self.deserialize_profile_items(raw_equipments["data"])
1408
1409        progressions_: typing.Optional[character.CharacterProgression] = None
1410        if raw_progressions := payload.get("progressions"):
1411            progressions_ = self.deserialize_character_progressions(
1412                raw_progressions["data"]
1413            )
1414
1415        render_data: typing.Optional[character.RenderedData] = None
1416        if raw_render_data := payload.get("renderData"):
1417            render_data = self.deserialize_character_render_data(
1418                raw_render_data["data"]
1419            )
1420
1421        character_records: typing.Optional[
1422            collections.Mapping[int, records.CharacterRecord]
1423        ] = None
1424        if raw_char_records := payload.get("records"):
1425            character_records = self.deserialize_characters_records(
1426                raw_char_records["data"]
1427            )
1428
1429        item_components: typing.Optional[components.ItemsComponent] = None
1430        if raw_item_components := payload.get("itemComponents"):
1431            item_components = self.deserialize_items_component(raw_item_components)
1432
1433        nodes: typing.Optional[collections.Mapping[int, records.Node]] = None
1434        if raw_nodes := payload.get("presentationNodes"):
1435            nodes = {
1436                int(node_hash): self._deserialize_node(node)
1437                for node_hash, node in raw_nodes["data"]["nodes"].items()
1438            }
1439
1440        collectibles: typing.Optional[items.Collectible] = None
1441        if raw_collectibles := payload.get("collectibles"):
1442            collectibles = self._deserialize_collectible(raw_collectibles["data"])
1443
1444        currency_lookups: typing.Optional[collections.Sequence[items.Currency]] = None
1445        if raw_currencies := payload.get("currencyLookups"):
1446            if "data" in raw_currencies:
1447                currency_lookups = self._deserialize_currencies(raw_currencies)
1448
1449        return components.CharacterComponent(
1450            activities=activities,
1451            equipment=equipment,
1452            inventory=inventory,
1453            progressions=progressions_,
1454            render_data=render_data,
1455            character=character_,
1456            character_records=character_records,
1457            profile_records=None,
1458            item_components=item_components,
1459            currency_lookups=currency_lookups,
1460            collectibles=collectibles,
1461            nodes=nodes,
1462        )
1463
1464    def _set_entity_attrs(
1465        self, payload: typedefs.JSONObject, *, key: str = "displayProperties"
1466    ) -> entity.Entity:
1467
1468        name: undefined.UndefinedOr[str] = undefined.UNDEFINED
1469        description: undefined.UndefinedOr[str] = undefined.UNDEFINED
1470
1471        if properties := payload[key]:
1472            if (raw_name := properties["name"]) is not typedefs.Unknown:
1473                name = raw_name
1474
1475            if (
1476                raw_description := properties["description"]
1477            ) and not typedefs.is_unknown(raw_description):
1478                description = raw_description
1479
1480        return entity.Entity(
1481            net=self._net,
1482            hash=payload["hash"],
1483            index=payload["index"],
1484            name=name,
1485            description=description,
1486            has_icon=properties["hasIcon"],
1487            icon=assets.Image(properties["icon"] if "icon" in properties else None),
1488        )
1489
1490    def deserialize_inventory_results(
1491        self, payload: typedefs.JSONObject
1492    ) -> iterators.Iterator[entity.SearchableEntity]:
1493        suggested_words: list[str] = payload["suggestedWords"]
1494
1495        def _check_unknown(s: str) -> undefined.UndefinedOr[str]:
1496            return s if not typedefs.is_unknown(s) else undefined.UNDEFINED
1497
1498        return iterators.Iterator(
1499            [
1500                entity.SearchableEntity(
1501                    net=self._net,
1502                    hash=data["hash"],
1503                    entity_type=data["entityType"],
1504                    weight=data["weight"],
1505                    suggested_words=suggested_words,
1506                    name=data["displayProperties"]["name"],
1507                    has_icon=data["displayProperties"]["hasIcon"],
1508                    description=_check_unknown(
1509                        data["displayProperties"]["description"]
1510                    ),
1511                    icon=assets.Image(data["displayProperties"]["icon"]),
1512                )
1513                for data in payload["results"]["results"]
1514            ]
1515        )
1516
1517    def _deserialize_inventory_item_objects(
1518        self, payload: typedefs.JSONObject
1519    ) -> entity.InventoryEntityObjects:
1520        return entity.InventoryEntityObjects(
1521            action=payload.get("action"),
1522            set_data=payload.get("setData"),
1523            stats=payload.get("stats"),
1524            equipping_block=payload.get("equippingBlock"),
1525            translation_block=payload.get("translationBlock"),
1526            preview=payload.get("preview"),
1527            quality=payload.get("quality"),
1528            value=payload.get("value"),
1529            source_data=payload.get("sourceData"),
1530            objectives=payload.get("objectives"),
1531            plug=payload.get("plug"),
1532            metrics=payload.get("metrics"),
1533            gearset=payload.get("gearset"),
1534            sack=payload.get("sack"),
1535            sockets=payload.get("sockets"),
1536            summary=payload.get("summary"),
1537            talent_gird=payload.get("talentGrid"),
1538            investments_stats=payload.get("investmentStats"),
1539            perks=payload.get("perks"),
1540            animations=payload.get("animations", []),
1541            links=payload.get("links", []),
1542        )
1543
1544    def deserialize_inventory_entity(  # noqa: C901 Too complex.
1545        self, payload: typedefs.JSONObject, /
1546    ) -> entity.InventoryEntity:
1547
1548        props = self._set_entity_attrs(payload)
1549        objects = self._deserialize_inventory_item_objects(payload)
1550
1551        collectible_hash: typing.Optional[int] = None
1552        if raw_collectible_hash := payload.get("collectibleHash"):
1553            collectible_hash = int(raw_collectible_hash)
1554
1555        secondary_icon: undefined.UndefinedOr[assets.Image] = undefined.UNDEFINED
1556        if raw_second_icon := payload.get("secondaryIcon"):
1557            secondary_icon = assets.Image(raw_second_icon)
1558
1559        secondary_overlay: undefined.UndefinedOr[assets.Image] = undefined.UNDEFINED
1560        if raw_second_overlay := payload.get("secondaryOverlay"):
1561            secondary_overlay = assets.Image(raw_second_overlay)
1562
1563        secondary_special: undefined.UndefinedOr[assets.Image] = undefined.UNDEFINED
1564        if raw_second_special := payload.get("secondarySpecial"):
1565            secondary_special = assets.Image(raw_second_special)
1566
1567        screenshot: undefined.UndefinedOr[assets.Image] = undefined.UNDEFINED
1568        if raw_screenshot := payload.get("screenshot"):
1569            screenshot = assets.Image(raw_screenshot)
1570
1571        watermark_icon: typing.Optional[assets.Image] = None
1572        if raw_watermark_icon := payload.get("iconWatermark"):
1573            watermark_icon = assets.Image(raw_watermark_icon)
1574
1575        watermark_shelved: typing.Optional[assets.Image] = None
1576        if raw_watermark_shelved := payload.get("iconWatermarkShelved"):
1577            watermark_shelved = assets.Image(raw_watermark_shelved)
1578
1579        about: undefined.UndefinedOr[str] = undefined.UNDEFINED
1580        if (raw_about := payload.get("flavorText")) and not typedefs.is_unknown(
1581            raw_about
1582        ):
1583            about = raw_about
1584
1585        ui_item_style: undefined.UndefinedOr[str] = undefined.UNDEFINED
1586        if (
1587            raw_ui_style := payload.get("uiItemDisplayStyle")
1588        ) and not typedefs.is_unknown(raw_ui_style):
1589            ui_item_style = raw_ui_style
1590
1591        tier_and_name: undefined.UndefinedOr[str] = undefined.UNDEFINED
1592        if (
1593            raw_tier_and_name := payload.get("itemTypeAndTierDisplayName")
1594        ) and not typedefs.is_unknown(raw_tier_and_name):
1595            tier_and_name = raw_tier_and_name
1596
1597        type_name: undefined.UndefinedOr[str] = undefined.UNDEFINED
1598        if (
1599            raw_type_name := payload.get("itemTypeDisplayName")
1600        ) and not typedefs.is_unknown(raw_type_name):
1601            type_name = raw_type_name
1602
1603        display_source: undefined.UndefinedOr[str] = undefined.UNDEFINED
1604        if (
1605            raw_display_source := payload.get("displaySource")
1606        ) and not typedefs.is_unknown(raw_display_source):
1607            display_source = raw_display_source
1608
1609        lorehash: typing.Optional[int] = None
1610        if raw_lore_hash := payload.get("loreHash"):
1611            lorehash = int(raw_lore_hash)
1612
1613        summary_hash: typing.Optional[int] = None
1614        if raw_summary_hash := payload.get("summaryItemHash"):
1615            summary_hash = raw_summary_hash
1616
1617        breaker_type_hash: typing.Optional[int] = None
1618        if raw_breaker_type_hash := payload.get("breakerTypeHash"):
1619            breaker_type_hash = int(raw_breaker_type_hash)
1620
1621        damage_types: typing.Optional[collections.Sequence[int]] = None
1622        if raw_damage_types := payload.get("damageTypes"):
1623            damage_types = [int(type_) for type_ in raw_damage_types]
1624
1625        damagetype_hashes: typing.Optional[collections.Sequence[int]] = None
1626        if raw_damagetype_hashes := payload.get("damageTypeHashes"):
1627            damagetype_hashes = [int(type_) for type_ in raw_damagetype_hashes]
1628
1629        default_damagetype_hash: typing.Optional[int] = None
1630        if raw_defaultdmg_hash := payload.get("defaultDamageTypeHash"):
1631            default_damagetype_hash = int(raw_defaultdmg_hash)
1632
1633        emblem_objective_hash: typing.Optional[int] = None
1634        if raw_emblem_obj_hash := payload.get("emblemObjectiveHash"):
1635            emblem_objective_hash = int(raw_emblem_obj_hash)
1636
1637        tier_type: typing.Optional[enums.TierType] = None
1638        tier: typing.Optional[enums.ItemTier] = None
1639        bucket_hash: typing.Optional[int] = None
1640        recovery_hash: typing.Optional[int] = None
1641        tier_name: undefined.UndefinedOr[str] = undefined.UNDEFINED
1642        isinstance_item: bool = False
1643        expire_tool_tip: undefined.UndefinedOr[str] = undefined.UNDEFINED
1644        expire_in_orbit_message: undefined.UndefinedOr[str] = undefined.UNDEFINED
1645        suppress_expiration: bool = False
1646        max_stack_size: typing.Optional[int] = None
1647        stack_label: undefined.UndefinedOr[str] = undefined.UNDEFINED
1648
1649        if inventory := payload.get("inventory"):
1650            tier_type = enums.TierType(int(inventory["tierType"]))
1651            tier = enums.ItemTier(int(inventory["tierTypeHash"]))
1652            bucket_hash = int(inventory["bucketTypeHash"])
1653            recovery_hash = int(inventory["recoveryBucketTypeHash"])
1654            tier_name = inventory["tierTypeName"]
1655            isinstance_item = inventory["isInstanceItem"]
1656            suppress_expiration = inventory["suppressExpirationWhenObjectivesComplete"]
1657            max_stack_size = int(inventory["maxStackSize"])
1658
1659            try:
1660                stack_label = inventory["stackUniqueLabel"]
1661            except KeyError:
1662                pass
1663
1664        return entity.InventoryEntity(
1665            net=self._net,
1666            collectible_hash=collectible_hash,
1667            name=props.name,
1668            about=about,
1669            emblem_objective_hash=emblem_objective_hash,
1670            suppress_expiration=suppress_expiration,
1671            max_stack_size=max_stack_size,
1672            stack_label=stack_label,
1673            tier=tier,
1674            tier_type=tier_type,
1675            tier_name=tier_name,
1676            bucket_hash=bucket_hash,
1677            recovery_bucket_hash=recovery_hash,
1678            isinstance_item=isinstance_item,
1679            expire_in_orbit_message=expire_in_orbit_message,
1680            expiration_tooltip=expire_tool_tip,
1681            lore_hash=lorehash,
1682            type_and_tier_name=tier_and_name,
1683            summary_hash=summary_hash,
1684            ui_display_style=ui_item_style,
1685            type_name=type_name,
1686            breaker_type_hash=breaker_type_hash,
1687            description=props.description,
1688            display_source=display_source,
1689            hash=props.hash,
1690            damage_types=damage_types,
1691            index=props.index,
1692            icon=props.icon,
1693            has_icon=props.has_icon,
1694            screenshot=screenshot,
1695            watermark_icon=watermark_icon,
1696            watermark_shelved=watermark_shelved,
1697            secondary_icon=secondary_icon,
1698            secondary_overlay=secondary_overlay,
1699            secondary_special=secondary_special,
1700            type=enums.ItemType(int(payload["itemType"])),
1701            trait_hashes=[int(id_) for id_ in payload.get("traitHashes", [])],
1702            trait_ids=[trait for trait in payload.get("traitIds", [])],
1703            category_hashes=[int(hash_) for hash_ in payload["itemCategoryHashes"]],
1704            item_class=enums.Class(int(payload["classType"])),
1705            sub_type=enums.ItemSubType(int(payload["itemSubType"])),
1706            breaker_type=int(payload["breakerType"]),
1707            default_damagetype=int(payload["defaultDamageType"]),
1708            default_damagetype_hash=default_damagetype_hash,
1709            damagetype_hashes=damagetype_hashes,
1710            tooltip_notifications=payload["tooltipNotifications"],
1711            not_transferable=payload["nonTransferrable"],
1712            allow_actions=payload["allowActions"],
1713            is_equippable=payload["equippable"],
1714            objects=objects,
1715            background_colors=payload.get("backgroundColor", {}),
1716            season_hash=payload.get("seasonHash"),
1717            has_postmaster_effect=payload["doesPostmasterPullHaveSideEffects"],
1718        )
1719
1720    def deserialize_objective_entity(
1721        self, payload: typedefs.JSONObject, /
1722    ) -> entity.ObjectiveEntity:
1723        props = self._set_entity_attrs(payload)
1724        return entity.ObjectiveEntity(
1725            net=self._net,
1726            hash=props.hash,
1727            index=props.index,
1728            description=props.description,
1729            name=props.name,
1730            has_icon=props.has_icon,
1731            icon=props.icon,
1732            unlock_value_hash=payload["unlockValueHash"],
1733            completion_value=payload["completionValue"],
1734            scope=entity.GatingScope(int(payload["scope"])),
1735            location_hash=payload["locationHash"],
1736            allowed_negative_value=payload["allowNegativeValue"],
1737            allowed_value_change=payload["allowValueChangeWhenCompleted"],
1738            counting_downward=payload["isCountingDownward"],
1739            value_style=entity.ValueUIStyle(int(payload["valueStyle"])),
1740            progress_description=payload["progressDescription"],
1741            perks=payload["perks"],
1742            stats=payload["stats"],
1743            minimum_visibility=payload["minimumVisibilityThreshold"],
1744            allow_over_completion=payload["allowOvercompletion"],
1745            show_value_style=payload["showValueOnComplete"],
1746            display_only_objective=payload["isDisplayOnlyObjective"],
1747            complete_value_style=entity.ValueUIStyle(
1748                int(payload["completedValueStyle"])
1749            ),
1750            progress_value_style=entity.ValueUIStyle(
1751                int(payload["inProgressValueStyle"])
1752            ),
1753            ui_label=payload["uiLabel"],
1754            ui_style=entity.ObjectiveUIStyle(int(payload["uiStyle"])),
1755        )
1756
1757    def _deserialize_activity_values(
1758        self, payload: typedefs.JSONObject, /
1759    ) -> activity.ActivityValues:
1760        team: typing.Optional[int] = None
1761        if raw_team := payload.get("team"):
1762            team = raw_team["basic"]["value"]
1763        return activity.ActivityValues(
1764            assists=payload["assists"]["basic"]["value"],
1765            deaths=payload["deaths"]["basic"]["value"],
1766            kills=payload["kills"]["basic"]["value"],
1767            is_completed=bool(payload["completed"]["basic"]["value"]),
1768            opponents_defeated=payload["opponentsDefeated"]["basic"]["value"],
1769            efficiency=payload["efficiency"]["basic"]["value"],
1770            kd_ratio=payload["killsDeathsRatio"]["basic"]["value"],
1771            kd_assists=payload["killsDeathsAssists"]["basic"]["value"],
1772            score=payload["score"]["basic"]["value"],
1773            duration=payload["activityDurationSeconds"]["basic"]["displayValue"],
1774            team=team,
1775            completion_reason=payload["completionReason"]["basic"]["displayValue"],
1776            fireteam_id=payload["fireteamId"]["basic"]["value"],
1777            start_seconds=payload["startSeconds"]["basic"]["value"],
1778            played_time=payload["timePlayedSeconds"]["basic"]["displayValue"],
1779            player_count=payload["playerCount"]["basic"]["value"],
1780            team_score=payload["teamScore"]["basic"]["value"],
1781        )
1782
1783    def deserialize_activity(
1784        self,
1785        payload: typedefs.JSONObject,
1786        /,
1787    ) -> activity.Activity:
1788        period = time.clean_date(payload["period"])
1789        details = payload["activityDetails"]
1790        ref_id = int(details["referenceId"])
1791        instance_id = int(details["instanceId"])
1792        mode = enums.GameMode(details["mode"])
1793        modes = [enums.GameMode(int(mode_)) for mode_ in details["modes"]]
1794        is_private = details["isPrivate"]
1795        membership_type = enums.MembershipType(int(details["membershipType"]))
1796
1797        # Since we're using the same fields for post activity method
1798        # this check is required since post activity doesn't values values
1799        values = self._deserialize_activity_values(payload["values"])
1800
1801        return activity.Activity(
1802            net=self._net,
1803            hash=ref_id,
1804            instance_id=instance_id,
1805            mode=mode,
1806            modes=modes,
1807            is_private=is_private,
1808            membership_type=membership_type,
1809            occurred_at=period,
1810            values=values,
1811        )
1812
1813    def deserialize_activities(
1814        self, payload: typedefs.JSONObject
1815    ) -> iterators.Iterator[activity.Activity]:
1816        return iterators.Iterator(
1817            [
1818                self.deserialize_activity(activity_)
1819                for activity_ in payload["activities"]
1820            ]
1821        )
1822
1823    def deserialize_extended_weapon_values(
1824        self, payload: typedefs.JSONObject
1825    ) -> activity.ExtendedWeaponValues:
1826
1827        assists: typing.Optional[int] = None
1828        if raw_assists := payload["values"].get("uniqueWeaponAssists"):
1829            assists = raw_assists["basic"]["value"]
1830        assists_damage: typing.Optional[int] = None
1831
1832        if raw_assists_damage := payload["values"].get("uniqueWeaponAssistDamage"):
1833            assists_damage = raw_assists_damage["basic"]["value"]
1834
1835        return activity.ExtendedWeaponValues(
1836            reference_id=int(payload["referenceId"]),
1837            kills=payload["values"]["uniqueWeaponKills"]["basic"]["value"],
1838            precision_kills=payload["values"]["uniqueWeaponPrecisionKills"]["basic"][
1839                "value"
1840            ],
1841            assists=assists,
1842            assists_damage=assists_damage,
1843            precision_kills_percentage=(
1844                payload["values"]["uniqueWeaponKillsPrecisionKills"]["basic"]["value"],
1845                payload["values"]["uniqueWeaponKillsPrecisionKills"]["basic"][
1846                    "displayValue"
1847                ],
1848            ),
1849        )
1850
1851    def _deserialize_extended_values(
1852        self, payload: typedefs.JSONObject
1853    ) -> activity.ExtendedValues:
1854        weapons: typing.Optional[
1855            collections.Collection[activity.ExtendedWeaponValues]
1856        ] = None
1857
1858        if raw_weapons := payload.get("weapons"):
1859            weapons = [
1860                self.deserialize_extended_weapon_values(value) for value in raw_weapons
1861            ]
1862
1863        return activity.ExtendedValues(
1864            precision_kills=payload["values"]["precisionKills"]["basic"]["value"],
1865            grenade_kills=payload["values"]["weaponKillsGrenade"]["basic"]["value"],
1866            melee_kills=payload["values"]["weaponKillsMelee"]["basic"]["value"],
1867            super_kills=payload["values"]["weaponKillsSuper"]["basic"]["value"],
1868            ability_kills=payload["values"]["weaponKillsAbility"]["basic"]["value"],
1869            weapons=weapons,
1870        )
1871
1872    def deserialize_post_activity_player(
1873        self, payload: typedefs.JSONObject, /
1874    ) -> activity.PostActivityPlayer:
1875        player = payload["player"]
1876
1877        class_hash: typedefs.NoneOr[int] = None
1878        if (class_hash := player.get("classHash")) is not None:
1879            class_hash = class_hash
1880
1881        race_hash: typedefs.NoneOr[int] = None
1882        if (race_hash := player.get("raceHash")) is not None:
1883            race_hash = race_hash
1884
1885        gender_hash: typedefs.NoneOr[int] = None
1886        if (gender_hash := player.get("genderHash")) is not None:
1887            gender_hash = gender_hash
1888
1889        character_class: undefined.UndefinedOr[str] = undefined.UNDEFINED
1890        if (
1891            character_class := player.get("characterClass")
1892        ) and not typedefs.is_unknown(character_class):
1893            character_class = character_class
1894
1895        character_level: typedefs.NoneOr[int] = None
1896        if (character_level := player.get("characterLevel")) is not None:
1897            character_level = character_level
1898
1899        return activity.PostActivityPlayer(
1900            standing=int(payload["standing"]),
1901            score=int(payload["score"]["basic"]["value"]),
1902            character_id=payload["characterId"],
1903            destiny_user=self.deserialize_destiny_membership(player["destinyUserInfo"]),
1904            character_class=character_class,
1905            character_level=character_level,
1906            race_hash=race_hash,
1907            gender_hash=gender_hash,
1908            class_hash=class_hash,
1909            light_level=int(player["lightLevel"]),
1910            emblem_hash=int(player["emblemHash"]),
1911            values=self._deserialize_activity_values(payload["values"]),
1912            extended_values=self._deserialize_extended_values(payload["extended"]),
1913        )
1914
1915    def _deserialize_post_activity_team(
1916        self, payload: typedefs.JSONObject
1917    ) -> activity.PostActivityTeam:
1918        return activity.PostActivityTeam(
1919            id=payload["teamId"],
1920            is_defeated=bool(payload["standing"]["basic"]["value"]),
1921            score=int(payload["score"]["basic"]["value"]),
1922            name=payload["teamName"],
1923        )
1924
1925    def deserialize_post_activity(
1926        self, payload: typedefs.JSONObject
1927    ) -> activity.PostActivity:
1928        period = time.clean_date(payload["period"])
1929        details = payload["activityDetails"]
1930        ref_id = int(details["referenceId"])
1931        instance_id = int(details["instanceId"])
1932        mode = enums.GameMode(details["mode"])
1933        modes = [enums.GameMode(int(mode_)) for mode_ in details["modes"]]
1934        is_private = details["isPrivate"]
1935        membership_type = enums.MembershipType(int(details["membershipType"]))
1936        return activity.PostActivity(
1937            net=self._net,
1938            hash=ref_id,
1939            membership_type=membership_type,
1940            instance_id=instance_id,
1941            mode=mode,
1942            modes=modes,
1943            is_private=is_private,
1944            occurred_at=period,
1945            starting_phase=int(payload["startingPhaseIndex"]),
1946            players=[
1947                self.deserialize_post_activity_player(player)
1948                for player in payload["entries"]
1949            ],
1950            teams=[
1951                self._deserialize_post_activity_team(team) for team in payload["teams"]
1952            ],
1953        )
1954
1955    def _deserialize_aggregated_activity_values(
1956        self, payload: typedefs.JSONObject
1957    ) -> activity.AggregatedActivityValues:
1958        # This ID is always the same for all aggregated values.
1959        activity_id = int(payload["fastestCompletionMsForActivity"]["activityId"])
1960
1961        return activity.AggregatedActivityValues(
1962            id=activity_id,
1963            fastest_completion_time=(
1964                int(payload["fastestCompletionMsForActivity"]["basic"]["value"]),
1965                payload["fastestCompletionMsForActivity"]["basic"]["displayValue"],
1966            ),
1967            completions=int(payload["activityCompletions"]["basic"]["value"]),
1968            kills=int(payload["activityKills"]["basic"]["value"]),
1969            deaths=int(payload["activityDeaths"]["basic"]["value"]),
1970            assists=int(payload["activityAssists"]["basic"]["value"]),
1971            seconds_played=(
1972                int(payload["activitySecondsPlayed"]["basic"]["value"]),
1973                payload["activitySecondsPlayed"]["basic"]["displayValue"],
1974            ),
1975            wins=int(payload["activityWins"]["basic"]["value"]),
1976            goals_missed=int(payload["activityGoalsMissed"]["basic"]["value"]),
1977            special_actions=int(payload["activitySpecialActions"]["basic"]["value"]),
1978            best_goals_hit=int(payload["activityBestGoalsHit"]["basic"]["value"]),
1979            best_single_score=int(
1980                payload["activityBestSingleGameScore"]["basic"]["value"]
1981            ),
1982            goals_hit=int(payload["activityGoalsHit"]["basic"]["value"]),
1983            special_score=int(payload["activitySpecialScore"]["basic"]["value"]),
1984            kd_assists=int(payload["activityKillsDeathsAssists"]["basic"]["value"]),
1985            kd_ratio=float(
1986                payload["activityKillsDeathsAssists"]["basic"]["displayValue"]
1987            ),
1988            precision_kills=int(payload["activityPrecisionKills"]["basic"]["value"]),
1989        )
1990
1991    def deserialize_aggregated_activity(
1992        self, payload: typedefs.JSONObject
1993    ) -> activity.AggregatedActivity:
1994        return activity.AggregatedActivity(
1995            hash=int(payload["activityHash"]),
1996            values=self._deserialize_aggregated_activity_values(payload["values"]),
1997        )
1998
1999    def deserialize_aggregated_activities(
2000        self, payload: typedefs.JSONObject
2001    ) -> iterators.Iterator[activity.AggregatedActivity]:
2002        return iterators.Iterator(
2003            [
2004                self.deserialize_aggregated_activity(activity)
2005                for activity in payload["activities"]
2006            ]
2007        )
2008
2009    def deserialize_linked_profiles(
2010        self, payload: typedefs.JSONObject
2011    ) -> profile.LinkedProfile:
2012        bungie_user = self.deserialize_partial_bungie_user(payload["bnetMembership"])
2013        error_profiles_vec: typing.MutableSequence[user.DestinyMembership] = []
2014        profiles_vec: typing.MutableSequence[user.DestinyMembership] = []
2015
2016        if raw_profile := payload.get("profiles"):
2017            for pfile in raw_profile:
2018                profiles_vec.append(self.deserialize_destiny_membership(pfile))
2019
2020        if raw_profiles_with_errors := payload.get("profilesWithErrors"):
2021            for raw_error_pfile in raw_profiles_with_errors:
2022                if error_pfile := raw_error_pfile.get("infoCard"):
2023                    error_profiles_vec.append(
2024                        self.deserialize_destiny_membership(error_pfile)
2025                    )
2026
2027        return profile.LinkedProfile(
2028            net=self._net,
2029            bungie=bungie_user,
2030            profiles=profiles_vec,
2031            profiles_with_errors=error_profiles_vec,
2032        )
2033
2034    def deserialize_clan_banners(
2035        self, payload: typedefs.JSONObject
2036    ) -> collections.Sequence[clans.ClanBanner]:
2037        banners_seq: typing.MutableSequence[clans.ClanBanner] = []
2038        if banners := payload.get("clanBannerDecals"):
2039            for k, v in banners.items():
2040                banner_obj = clans.ClanBanner(
2041                    id=int(k),
2042                    foreground=assets.Image(v["foregroundPath"]),
2043                    background=assets.Image(v["backgroundPath"]),
2044                )
2045                banners_seq.append(banner_obj)
2046        return banners_seq
2047
2048    def deserialize_public_milestone_content(
2049        self, payload: typedefs.JSONObject
2050    ) -> milestones.MilestoneContent:
2051        items_categoris: typedefs.NoneOr[milestones.MilestoneItems] = None
2052        if raw_categories := payload.get("itemCategories"):
2053            for item in raw_categories:
2054                title = undefined.UNDEFINED
2055                if raw_title := item.get("title"):
2056                    if raw_title != typedefs.Unknown:
2057                        title = raw_title
2058                if raw_hashes := item.get("itemHashes"):
2059                    hashes: collections.Sequence[int] = raw_hashes
2060
2061                items_categoris = milestones.MilestoneItems(title=title, hashes=hashes)
2062
2063        about = undefined.UNDEFINED
2064        if (raw_about := payload["about"]) != typedefs.Unknown:
2065            about = raw_about
2066
2067        status = undefined.UNDEFINED
2068        if (raw_status := payload["status"]) != typedefs.Unknown:
2069            status = raw_status
2070
2071        tips: typing.MutableSequence[undefined.UndefinedOr[str]] = []
2072        if raw_tips := payload.get("tips"):
2073            for raw_tip in raw_tips:
2074                if raw_tip == typedefs.Unknown:
2075                    raw_tip = undefined.UNDEFINED
2076                tips.append(raw_tip)
2077
2078        return milestones.MilestoneContent(
2079            about=about, status=status, tips=tips, items=items_categoris
2080        )
2081
2082    def deserialize_friend(self, payload: typedefs.JSONObject, /) -> friends.Friend:
2083        name = undefined.UNDEFINED
2084        if (raw_name := payload["bungieGlobalDisplayName"]) != typedefs.Unknown:
2085            name = raw_name
2086
2087        bungie_user: typedefs.NoneOr[user.BungieUser] = None
2088
2089        if raw_bungie_user := payload.get("bungieNetUser"):
2090            bungie_user = self.deserialize_bungie_user(raw_bungie_user)
2091
2092        return friends.Friend(
2093            net=self._net,
2094            id=int(payload["lastSeenAsMembershipId"]),
2095            name=name,
2096            code=payload.get("bungieGlobalDisplayNameCode"),
2097            relationship=enums.Relationship(payload["relationship"]),
2098            user=bungie_user,
2099            online_status=enums.Presence(payload["onlineStatus"]),
2100            online_title=payload["onlineTitle"],
2101            type=enums.MembershipType(payload["lastSeenAsBungieMembershipType"]),
2102        )
2103
2104    def deserialize_friends(
2105        self, payload: typedefs.JSONObject
2106    ) -> collections.Sequence[friends.Friend]:
2107        mut_seq: typing.MutableSequence[friends.Friend] = []
2108        if raw_friends := payload.get("friends"):
2109            for friend in raw_friends:
2110                mut_seq.append(self.deserialize_friend(friend))
2111        return mut_seq
2112
2113    def deserialize_friend_requests(
2114        self, payload: typedefs.JSONObject
2115    ) -> friends.FriendRequestView:
2116        incoming: typing.MutableSequence[friends.Friend] = []
2117        outgoing: typing.MutableSequence[friends.Friend] = []
2118
2119        if raw_incoming_requests := payload.get("incomingRequests"):
2120            for incoming_request in raw_incoming_requests:
2121                incoming.append(self.deserialize_friend(incoming_request))
2122
2123        if raw_outgoing_requests := payload.get("outgoingRequests"):
2124            for outgoing_request in raw_outgoing_requests:
2125                outgoing.append(self.deserialize_friend(outgoing_request))
2126
2127        return friends.FriendRequestView(incoming=incoming, outgoing=outgoing)
2128
2129    def _set_fireteam_fields(
2130        self, payload: typedefs.JSONObject, total_results: typing.Optional[int] = None
2131    ) -> fireteams.Fireteam:
2132        activity_type = fireteams.FireteamActivity(payload["activityType"])
2133        return fireteams.Fireteam(
2134            id=int(payload["fireteamId"]),
2135            group_id=int(payload["groupId"]),
2136            platform=fireteams.FireteamPlatform(payload["platform"]),
2137            is_immediate=payload["isImmediate"],
2138            activity_type=activity_type,
2139            owner_id=int(payload["ownerMembershipId"]),
2140            player_slot_count=payload["playerSlotCount"],
2141            available_player_slots=payload["availablePlayerSlotCount"],
2142            available_alternate_slots=payload["availableAlternateSlotCount"],
2143            title=payload["title"],
2144            date_created=time.clean_date(payload["dateCreated"]),
2145            is_public=payload["isPublic"],
2146            locale=fireteams.FireteamLanguage(payload["locale"]),
2147            is_valid=payload["isValid"],
2148            last_modified=time.clean_date(payload["datePlayerModified"]),
2149            total_results=total_results or 0,
2150        )
2151
2152    def deserialize_fireteams(
2153        self, payload: typedefs.JSONObject
2154    ) -> typedefs.NoneOr[collections.Sequence[fireteams.Fireteam]]:
2155        fireteams_: typing.MutableSequence[fireteams.Fireteam] = []
2156
2157        result: list[typedefs.JSONObject]
2158        if not (result := payload["results"]):
2159            return None
2160        for elem in result:
2161            fireteams_.append(
2162                self._set_fireteam_fields(
2163                    elem, total_results=int(payload["totalResults"])
2164                )
2165            )
2166        return fireteams_
2167
2168    def deserialize_fireteam_destiny_users(
2169        self, payload: typedefs.JSONObject
2170    ) -> fireteams.FireteamUser:
2171        destiny_obj = self.deserialize_destiny_membership(payload)
2172        # We could helpers.just return a DestinyMembership object but this is
2173        # missing the fireteam display name and id fields.
2174        return fireteams.FireteamUser(
2175            net=self._net,
2176            id=destiny_obj.id,
2177            code=destiny_obj.code,
2178            icon=destiny_obj.icon,
2179            types=destiny_obj.types,
2180            type=destiny_obj.type,
2181            is_public=destiny_obj.is_public,
2182            crossave_override=destiny_obj.crossave_override,
2183            name=destiny_obj.name,
2184            last_seen_name=destiny_obj.last_seen_name,
2185            fireteam_display_name=payload["FireteamDisplayName"],
2186            fireteam_membership_id=enums.MembershipType(
2187                payload["FireteamMembershipType"]
2188            ),
2189        )
2190
2191    def deserialize_fireteam_members(
2192        self, payload: typedefs.JSONObject, *, alternatives: bool = False
2193    ) -> typing.Optional[collections.Sequence[fireteams.FireteamMember]]:
2194        members_: list[fireteams.FireteamMember] = []
2195        if members := payload.get("Members" if not alternatives else "Alternates"):
2196            for member in members:
2197                bungie_fields = self.deserialize_partial_bungie_user(member)
2198                members_fields = fireteams.FireteamMember(
2199                    destiny_user=self.deserialize_fireteam_destiny_users(member),
2200                    has_microphone=member["hasMicrophone"],
2201                    character_id=int(member["characterId"]),
2202                    date_joined=time.clean_date(member["dateJoined"]),
2203                    last_platform_invite_date=time.clean_date(
2204                        member["lastPlatformInviteAttemptDate"]
2205                    ),
2206                    last_platform_invite_result=int(
2207                        member["lastPlatformInviteAttemptResult"]
2208                    ),
2209                    net=self._net,
2210                    name=bungie_fields.name,
2211                    id=bungie_fields.id,
2212                    icon=bungie_fields.icon,
2213                    is_public=bungie_fields.is_public,
2214                    crossave_override=bungie_fields.crossave_override,
2215                    types=bungie_fields.types,
2216                    type=bungie_fields.type,
2217                )
2218                members_.append(members_fields)
2219        else:
2220            return None
2221        return members_
2222
2223    def deserialize_available_fireteams(
2224        self,
2225        data: typedefs.JSONObject,
2226        *,
2227        no_results: bool = False,
2228    ) -> typing.Union[
2229        fireteams.AvailableFireteam, collections.Sequence[fireteams.AvailableFireteam]
2230    ]:
2231        fireteams_: list[fireteams.AvailableFireteam] = []
2232
2233        # This needs to be used outside the results
2234        # JSON key.
2235        if no_results is True:
2236            payload = data
2237
2238        if result := payload.get("results"):
2239
2240            for fireteam in result:
2241                found_fireteams = self._set_fireteam_fields(fireteam["Summary"])
2242                fireteams_fields = fireteams.AvailableFireteam(
2243                    id=found_fireteams.id,
2244                    group_id=found_fireteams.group_id,
2245                    platform=found_fireteams.platform,
2246                    activity_type=found_fireteams.activity_type,
2247                    is_immediate=found_fireteams.is_immediate,
2248                    is_public=found_fireteams.is_public,
2249                    is_valid=found_fireteams.is_valid,
2250                    owner_id=found_fireteams.owner_id,
2251                    player_slot_count=found_fireteams.player_slot_count,
2252                    available_player_slots=found_fireteams.available_player_slots,
2253                    available_alternate_slots=found_fireteams.available_alternate_slots,
2254                    title=found_fireteams.title,
2255                    date_created=found_fireteams.date_created,
2256                    locale=found_fireteams.locale,
2257                    last_modified=found_fireteams.last_modified,
2258                    total_results=found_fireteams.total_results,
2259                    members=self.deserialize_fireteam_members(payload),
2260                    alternatives=self.deserialize_fireteam_members(
2261                        payload, alternatives=True
2262                    ),
2263                )
2264            fireteams_.append(fireteams_fields)
2265            if no_results:
2266                return fireteams_fields
2267        return fireteams_
2268
2269    def deserialize_fireteam_party(
2270        self, payload: typedefs.JSONObject
2271    ) -> fireteams.FireteamParty:
2272        last_destination_hash: typing.Optional[int] = None
2273        if raw_dest_hash := payload.get("lastOrbitedDestinationHash"):
2274            last_destination_hash = int(raw_dest_hash)
2275
2276        return fireteams.FireteamParty(
2277            members=[
2278                self._deserialize_fireteam_party_member(member)
2279                for member in payload["partyMembers"]
2280            ],
2281            activity=self._deserialize_fireteam_party_current_activity(
2282                payload["currentActivity"]
2283            ),
2284            settings=self._deserialize_fireteam_party_settings(payload["joinability"]),
2285            last_destination_hash=last_destination_hash,
2286            tracking=payload["tracking"],
2287        )
2288
2289    def _deserialize_fireteam_party_member(
2290        self, payload: typedefs.JSONObject
2291    ) -> fireteams.FireteamPartyMember:
2292
2293        status = fireteams.FireteamPartyMemberState(payload["status"])
2294        displayname: undefined.UndefinedOr[str] = undefined.UNDEFINED
2295        if raw_name := payload.get("displayName"):
2296            displayname = raw_name
2297
2298        return fireteams.FireteamPartyMember(
2299            membership_id=int(payload["membershipId"]),
2300            emblem_hash=int(payload["emblemHash"]),
2301            status=status,
2302            display_name=displayname,
2303        )
2304
2305    def _deserialize_fireteam_party_current_activity(
2306        self, payload: typedefs.JSONObject
2307    ) -> fireteams.FireteamPartyCurrentActivity:
2308        start_date: typing.Optional[datetime.datetime] = None
2309        if raw_start_date := payload.get("startTime"):
2310            start_date = time.clean_date(raw_start_date)
2311
2312        end_date: typing.Optional[datetime.datetime] = None
2313        if raw_end_date := payload.get("endTime"):
2314            end_date = time.clean_date(raw_end_date)
2315        return fireteams.FireteamPartyCurrentActivity(
2316            start_time=start_date,
2317            end_time=end_date,
2318            score=float(payload["score"]),
2319            highest_opposing_score=float(payload["highestOpposingFactionScore"]),
2320            opponenst_count=int(payload["numberOfOpponents"]),
2321            player_count=int(payload["numberOfPlayers"]),
2322        )
2323
2324    def _deserialize_fireteam_party_settings(
2325        self, payload: typedefs.JSONObject
2326    ) -> fireteams.FireteamPartySettings:
2327        closed_reasons = enums.ClosedReasons(payload["closedReasons"])
2328        return fireteams.FireteamPartySettings(
2329            open_slots=int(payload["openSlots"]),
2330            privacy_setting=enums.PrivacySetting(int(payload["privacySetting"])),
2331            closed_reasons=closed_reasons,
2332        )
2333
2334    def deserialize_seasonal_artifact(
2335        self, payload: typedefs.JSONObject
2336    ) -> season.Artifact:
2337        if raw_artifact := payload.get("seasonalArtifact"):
2338            if points := raw_artifact.get("pointProgression"):
2339                points_prog = progressions.Progression(
2340                    hash=points["progressionHash"],
2341                    level=points["level"],
2342                    cap=points["levelCap"],
2343                    daily_limit=points["dailyLimit"],
2344                    weekly_limit=points["weeklyLimit"],
2345                    current_progress=points["currentProgress"],
2346                    daily_progress=points["dailyProgress"],
2347                    needed=points["progressToNextLevel"],
2348                    next_level=points["nextLevelAt"],
2349                )
2350
2351            if bonus := raw_artifact.get("powerBonusProgression"):
2352                power_bonus_prog = progressions.Progression(
2353                    hash=bonus["progressionHash"],
2354                    level=bonus["level"],
2355                    cap=bonus["levelCap"],
2356                    daily_limit=bonus["dailyLimit"],
2357                    weekly_limit=bonus["weeklyLimit"],
2358                    current_progress=bonus["currentProgress"],
2359                    daily_progress=bonus["dailyProgress"],
2360                    needed=bonus["progressToNextLevel"],
2361                    next_level=bonus["nextLevelAt"],
2362                )
2363            artifact = season.Artifact(
2364                net=self._net,
2365                hash=raw_artifact["artifactHash"],
2366                power_bonus=raw_artifact["powerBonus"],
2367                acquired_points=raw_artifact["pointsAcquired"],
2368                bonus=power_bonus_prog,
2369                points=points_prog,
2370            )
2371        return artifact
2372
2373    def deserialize_profile_progression(
2374        self, payload: typedefs.JSONObject
2375    ) -> profile.ProfileProgression:
2376        return profile.ProfileProgression(
2377            artifact=self.deserialize_seasonal_artifact(payload["data"]),
2378            checklist={
2379                int(check_id): checklists
2380                for check_id, checklists in payload["data"]["checklists"].items()
2381            },
2382        )
2383
2384    def deserialize_instanced_item(
2385        self, payload: typedefs.JSONObject
2386    ) -> items.ItemInstance:
2387        damage_type_hash: typing.Optional[int] = None
2388        if raw_damagetype_hash := payload.get("damageTypeHash"):
2389            damage_type_hash = int(raw_damagetype_hash)
2390
2391        required_hashes: typing.Optional[collections.Collection[int]] = None
2392        if raw_required_hashes := payload.get("unlockHashesRequiredToEquip"):
2393            required_hashes = [int(raw_hash) for raw_hash in raw_required_hashes]
2394
2395        breaker_type: typing.Optional[items.ItemBreakerType] = None
2396        if raw_break_type := payload.get("breakerType"):
2397            breaker_type = items.ItemBreakerType(int(raw_break_type))
2398
2399        breaker_type_hash: typing.Optional[int] = None
2400        if raw_break_type_hash := payload.get("breakerTypeHash"):
2401            breaker_type_hash = int(raw_break_type_hash)
2402
2403        energy: typing.Optional[items.ItemEnergy] = None
2404        if raw_energy := payload.get("energy"):
2405            energy = self.deserialize_item_energy(raw_energy)
2406
2407        primary_stats = None
2408        if raw_primary_stats := payload.get("primaryStat"):
2409            primary_stats = self.deserialize_item_stats_view(raw_primary_stats)
2410
2411        return items.ItemInstance(
2412            damage_type=enums.DamageType(int(payload["damageType"])),
2413            damage_type_hash=damage_type_hash,
2414            primary_stat=primary_stats,
2415            item_level=int(payload["itemLevel"]),
2416            quality=int(payload["quality"]),
2417            is_equipped=payload["isEquipped"],
2418            can_equip=payload["canEquip"],
2419            equip_required_level=int(payload["equipRequiredLevel"]),
2420            required_equip_unlock_hashes=required_hashes,
2421            cant_equip_reason=int(payload["cannotEquipReason"]),
2422            breaker_type=breaker_type,
2423            breaker_type_hash=breaker_type_hash,
2424            energy=energy,
2425        )
2426
2427    def deserialize_item_energy(self, payload: typedefs.JSONObject) -> items.ItemEnergy:
2428        energy_hash: typing.Optional[int] = None
2429        if raw_energy_hash := payload.get("energyTypeHash"):
2430            energy_hash = int(raw_energy_hash)
2431
2432        return items.ItemEnergy(
2433            hash=energy_hash,
2434            type=items.ItemEnergyType(int(payload["energyType"])),
2435            capacity=int(payload["energyCapacity"]),
2436            used_energy=int(payload["energyUsed"]),
2437            unused_energy=int(payload["energyUnused"]),
2438        )
2439
2440    def deserialize_item_perk(self, payload: typedefs.JSONObject) -> items.ItemPerk:
2441        perk_hash: typing.Optional[int] = None
2442        if raw_perk_hash := payload.get("perkHash"):
2443            perk_hash = int(raw_perk_hash)
2444
2445        return items.ItemPerk(
2446            hash=perk_hash,
2447            icon=assets.Image(payload["iconPath"]),
2448            is_active=payload["isActive"],
2449            is_visible=payload["visible"],
2450        )
2451
2452    def deserialize_item_socket(self, payload: typedefs.JSONObject) -> items.ItemSocket:
2453        plug_hash: typing.Optional[int] = None
2454        if raw_plug_hash := payload.get("plugHash"):
2455            plug_hash = int(raw_plug_hash)
2456
2457        enable_fail_indexes: typing.Optional[list[int]] = None
2458        if raw_indexes := payload.get("enableFailIndexes"):
2459            enable_fail_indexes = [int(index) for index in raw_indexes]
2460
2461        return items.ItemSocket(
2462            plug_hash=plug_hash,
2463            is_enabled=payload["isEnabled"],
2464            enable_fail_indexes=enable_fail_indexes,
2465            is_visible=payload.get("visible"),
2466        )
2467
2468    def deserialize_item_stats_view(
2469        self, payload: typedefs.JSONObject
2470    ) -> items.ItemStatsView:
2471        return items.ItemStatsView(
2472            stat_hash=payload.get("statHash"), value=payload.get("value")
2473        )
2474
2475    def deserialize_plug_item_state(
2476        self, payload: typedefs.JSONObject
2477    ) -> items.PlugItemState:
2478        item_hash: typing.Optional[int] = None
2479        if raw_item_hash := payload.get("plugItemHash"):
2480            item_hash = int(raw_item_hash)
2481
2482        insert_fail_indexes: typedefs.NoneOr[list[int]] = None
2483        if raw_fail_indexes := payload.get("insertFailIndexes"):
2484            insert_fail_indexes = [int(k) for k in raw_fail_indexes]
2485
2486        enable_fail_indexes: typedefs.NoneOr[list[int]] = None
2487        if raw_enabled_indexes := payload.get("enableFailIndexes"):
2488            enable_fail_indexes = [int(k) for k in raw_enabled_indexes]
2489
2490        return items.PlugItemState(
2491            item_hash=item_hash,
2492            insert_fail_indexes=insert_fail_indexes,
2493            enable_fail_indexes=enable_fail_indexes,
2494            is_enabled=payload["enabled"],
2495            can_insert=payload["canInsert"],
2496        )

The base deserialization factory class for all aiobungie objects.

Highly inspired hikari entity factory used to deserialize JSON responses from the REST client and turning them into a aiobungie.crates Python classes.

Factory(net: aiobungie.traits.Netrunner)
70    def __init__(self, net: traits.Netrunner) -> None:
71        self._net = net
def deserialize_bungie_user(self, data: dict[str, typing.Any]) -> aiobungie.crates.user.BungieUser:
73    def deserialize_bungie_user(self, data: typedefs.JSONObject) -> user.BungieUser:
74        return user.BungieUser(
75            id=int(data["membershipId"]),
76            created_at=time.clean_date(data["firstAccess"]),
77            name=data.get("cachedBungieGlobalDisplayName", undefined.UNDEFINED),
78            is_deleted=data["isDeleted"],
79            about=data["about"],
80            updated_at=time.clean_date(data["lastUpdate"]),
81            psn_name=data.get("psnDisplayName", None),
82            stadia_name=data.get("stadiaDisplayName", None),
83            steam_name=data.get("steamDisplayName", None),
84            twitch_name=data.get("twitchDisplayName", None),
85            blizzard_name=data.get("blizzardDisplayName", None),
86            status=data["statusText"],
87            locale=data["locale"],
88            picture=assets.Image(path=str(data["profilePicturePath"])),
89            code=data.get("cachedBungieGlobalDisplayNameCode", None),
90            unique_name=data.get("uniqueName", None),
91            theme_id=int(data["profileTheme"]),
92            show_activity=bool(data["showActivity"]),
93            theme_name=data["profileThemeName"],
94            display_title=data["userTitleDisplay"],
95        )

Deserialize a raw JSON Bungie.net user only payload into a user object.

This only returns the Bungie.net user and not the Destiny memberships.

Parameters
Returns
def deserialize_partial_bungie_user( self, payload: dict[str, typing.Any]) -> aiobungie.crates.user.PartialBungieUser:
 97    def deserialize_partial_bungie_user(
 98        self, payload: typedefs.JSONObject
 99    ) -> user.PartialBungieUser:
100        return user.PartialBungieUser(
101            net=self._net,
102            types=[
103                enums.MembershipType(type_)
104                for type_ in payload.get("applicableMembershipTypes", [])
105            ],
106            name=payload.get("displayName", undefined.UNDEFINED),
107            id=int(payload["membershipId"]),
108            crossave_override=enums.MembershipType(payload["crossSaveOverride"]),
109            is_public=payload["isPublic"],
110            icon=assets.Image(payload.get("iconPath", "")),
111            type=enums.MembershipType(payload["membershipType"]),
112        )

Deserialize a raw JSON of a partial bungieNetUserInfo.

A partial user is a bungie.net user payload with missing information from the main BungieUser object.

Parameters
Returns
def deserialize_destiny_membership( self, payload: dict[str, typing.Any]) -> aiobungie.crates.user.DestinyMembership:
114    def deserialize_destiny_membership(
115        self, payload: typedefs.JSONObject
116    ) -> user.DestinyMembership:
117        name: undefined.UndefinedOr[str] = undefined.UNDEFINED
118        if (
119            raw_name := payload.get("bungieGlobalDisplayName", "")
120        ) and not typedefs.is_unknown(raw_name):
121            name = raw_name
122
123        return user.DestinyMembership(
124            net=self._net,
125            id=int(payload["membershipId"]),
126            name=name,
127            code=payload.get("bungieGlobalDisplayNameCode", None),
128            last_seen_name=payload.get("LastSeenDisplayName")
129            or payload.get("displayName")  # noqa: W503
130            or "",  # noqa: W503
131            type=enums.MembershipType(payload["membershipType"]),
132            is_public=payload["isPublic"],
133            crossave_override=enums.MembershipType(payload["crossSaveOverride"]),
134            icon=assets.Image(payload.get("iconPath", "")),
135            types=[
136                enums.MembershipType(type_)
137                for type_ in payload.get("applicableMembershipTypes", [])
138            ],
139        )

Deserialize a raw JSON of destinyUserInfo destiny membership information.

Parameters
Returns
  • aiobungie.crates.user.DestinyMembership: A Destiny 2 membership.
def deserialize_destiny_memberships( self, data: list[typing.Any]) -> collections.abc.Sequence[aiobungie.crates.user.DestinyMembership]:
141    def deserialize_destiny_memberships(
142        self, data: typedefs.JSONArray
143    ) -> collections.Sequence[user.DestinyMembership]:
144        return [self.deserialize_destiny_membership(membership) for membership in data]

Deserialize a raw JSON payload/array of destinyUserInfo.

Parameters
Returns
  • collections.Sequence[aiobungie.crates.user.DestinyMembership]: A sequence of Destiny 2 memberships.
def deserialize_user(self, data: dict[str, typing.Any]) -> aiobungie.crates.user.User:
146    def deserialize_user(self, data: typedefs.JSONObject) -> user.User:
147
148        primary_membership_id: typing.Optional[int] = None
149        if raw_primary_id := data.get("primaryMembershipId"):
150            primary_membership_id = int(raw_primary_id)
151
152        return user.User(
153            bungie=self.deserialize_bungie_user(data["bungieNetUser"]),
154            destiny=self.deserialize_destiny_memberships(data["destinyMemberships"]),
155            primary_membership_id=primary_membership_id,
156        )

Deserialize a raw JSON results of fetched user memberships and Bungie.net user its their id.

Parameters
Returns
def deserialize_searched_user( self, payload: dict[str, typing.Any]) -> aiobungie.crates.user.SearchableDestinyUser:
158    def deserialize_searched_user(
159        self, payload: typedefs.JSONObject
160    ) -> user.SearchableDestinyUser:
161        name: undefined.UndefinedOr[str] = undefined.UNDEFINED
162        if (raw_name := payload["bungieGlobalDisplayName"]) and not typedefs.is_unknown(
163            raw_name
164        ):
165            name = raw_name
166
167        code: typing.Optional[int] = None
168        if raw_code := payload.get("bungieGlobalDisplayNameCode"):
169            code = int(raw_code)
170
171        bungie_id: typing.Optional[int] = None
172        if raw_bungie_id := payload.get("bungieNetMembershipId"):
173            bungie_id = int(raw_bungie_id)
174
175        return user.SearchableDestinyUser(
176            name=name,
177            code=code,
178            bungie_id=bungie_id,
179            memberships=self.deserialize_destiny_memberships(
180                payload["destinyMemberships"]
181            ),
182        )

Deserialize the results of user search details.

Parameters
Returns
def deserialize_user_credentials( self, payload: list[typing.Any]) -> collections.abc.Sequence[aiobungie.crates.user.UserCredentials]:
184    def deserialize_user_credentials(
185        self, payload: typedefs.JSONArray
186    ) -> collections.Sequence[user.UserCredentials]:
187        return [
188            user.UserCredentials(
189                type=enums.CredentialType(int(creds["credentialType"])),
190                display_name=creds["credentialDisplayName"],
191                is_public=creds["isPublic"],
192                self_as_string=creds.get("credentialAsString", undefined.UNDEFINED),
193            )
194            for creds in payload
195        ]

Deserialize a JSON array of Bungie user credentials.

Parameters
Returns
def deserialize_user_themes( self, payload: list[typing.Any]) -> collections.abc.Sequence[aiobungie.crates.user.UserThemes]:
197    def deserialize_user_themes(
198        self, payload: typedefs.JSONArray
199    ) -> collections.Sequence[user.UserThemes]:
200        return [
201            user.UserThemes(
202                id=int(entry["userThemeId"]),
203                name=entry["userThemeName"]
204                if "userThemeName" in entry
205                else undefined.UNDEFINED,
206                description=entry["userThemeDescription"]
207                if "userThemeDescription" in entry
208                else undefined.UNDEFINED,
209            )
210            for entry in payload
211        ]

Deserialize a raw JSON array of Bungie user themes.

Parameters
Returns
  • collections.Sequence[aiobungie.crates.user.UserThemes]: A sequence of bungie user themes.
def deserialize_clan(self, payload: dict[str, typing.Any]) -> aiobungie.crates.clans.Clan:
213    def deserialize_clan(self, payload: typedefs.JSONObject) -> clans.Clan:
214
215        # This is kinda redundant
216        data = payload
217
218        # This is always outside the details.
219        current_user_map: typing.Optional[
220            collections.Mapping[str, clans.ClanMember]
221        ] = None
222        if raw_current_user_map := payload.get("currentUserMemberMap"):
223            current_user_map = {
224                membership_type: self.deserialize_clan_member(membership)
225                for membership_type, membership in raw_current_user_map.items()
226            }
227
228        try:
229            data = payload["detail"]
230        except KeyError:
231            pass
232
233        id = data["groupId"]
234        name = data["name"]
235        created_at = data["creationDate"]
236        member_count = data["memberCount"]
237        about = data["about"]
238        motto = data["motto"]
239        is_public = data["isPublic"]
240        banner = assets.Image(str(data["bannerPath"]))
241        avatar = assets.Image(str(data["avatarPath"]))
242        tags = data["tags"]
243        type = data["groupType"]
244
245        features = data["features"]
246        features_obj = clans.ClanFeatures(
247            max_members=features["maximumMembers"],
248            max_membership_types=features["maximumMembershipsOfGroupType"],
249            capabilities=features["capabilities"],
250            membership_types=features["membershipTypes"],
251            invite_permissions=features["invitePermissionOverride"],
252            update_banner_permissions=features["updateBannerPermissionOverride"],
253            update_culture_permissions=features["updateCulturePermissionOverride"],
254            join_level=features["joinLevel"],
255        )
256
257        information: typedefs.JSONObject = data["clanInfo"]
258        progression: collections.Mapping[int, progressions.Progression] = {
259            int(prog_hash): self.deserialize_progressions(prog)
260            for prog_hash, prog in information["d2ClanProgressions"].items()
261        }
262
263        founder: typedefs.NoneOr[clans.ClanMember] = None
264        if raw_founder := payload.get("founder"):
265            founder = self.deserialize_clan_member(raw_founder)
266
267        return clans.Clan(
268            net=self._net,
269            id=int(id),
270            name=name,
271            type=enums.GroupType(type),
272            created_at=time.clean_date(created_at),
273            member_count=member_count,
274            motto=motto,
275            about=about,
276            is_public=is_public,
277            banner=banner,
278            avatar=avatar,
279            tags=tags,
280            features=features_obj,
281            owner=founder,
282            progressions=progression,
283            call_sign=information["clanCallsign"],
284            banner_data=information["clanBannerData"],
285            chat_security=data["chatSecurity"],
286            conversation_id=int(data["conversationId"]),
287            allow_chat=data["allowChat"],
288            theme=data["theme"],
289            current_user_membership=current_user_map,
290        )

Deserialize a raw JSON payload of Bungie clan information.

Parameters
Returns
def deserialize_clan_member( self, data: dict[str, typing.Any], /) -> aiobungie.crates.clans.ClanMember:
292    def deserialize_clan_member(self, data: typedefs.JSONObject, /) -> clans.ClanMember:
293        destiny_user = self.deserialize_destiny_membership(data["destinyUserInfo"])
294        return clans.ClanMember(
295            net=self._net,
296            last_seen_name=destiny_user.last_seen_name,
297            id=destiny_user.id,
298            name=destiny_user.name,
299            icon=destiny_user.icon,
300            last_online=time.from_timestamp(int(data["lastOnlineStatusChange"])),
301            group_id=int(data["groupId"]),
302            joined_at=time.clean_date(data["joinDate"]),
303            types=destiny_user.types,
304            is_public=destiny_user.is_public,
305            type=destiny_user.type,
306            code=destiny_user.code,
307            is_online=data["isOnline"],
308            crossave_override=destiny_user.crossave_override,
309            bungie=self.deserialize_partial_bungie_user(data["bungieNetUserInfo"])
310            if "bungieNetUserInfo" in data
311            else None,
312            member_type=enums.ClanMemberType(int(data["memberType"])),
313        )

Deserialize a JSON payload of a clan member information.

Parameters
Returns
def deserialize_clan_members( self, data: dict[str, typing.Any], /) -> aiobungie.Iterator[aiobungie.crates.clans.ClanMember]:
315    def deserialize_clan_members(
316        self, data: typedefs.JSONObject, /
317    ) -> iterators.Iterator[clans.ClanMember]:
318        return iterators.Iterator(
319            [self.deserialize_clan_member(member) for member in data["results"]]
320        )

Deserialize a JSON payload of a clan members information.

Parameters
Returns
def deserialize_group_member( self, payload: dict[str, typing.Any]) -> aiobungie.crates.clans.GroupMember:
322    def deserialize_group_member(
323        self, payload: typedefs.JSONObject
324    ) -> clans.GroupMember:
325        member = payload["member"]
326        return clans.GroupMember(
327            net=self._net,
328            join_date=time.clean_date(member["joinDate"]),
329            group_id=int(member["groupId"]),
330            member_type=enums.ClanMemberType(member["memberType"]),
331            is_online=member["isOnline"],
332            last_online=time.from_timestamp(int(member["lastOnlineStatusChange"])),
333            inactive_memberships=payload.get("areAllMembershipsInactive", None),
334            member=self.deserialize_destiny_membership(member["destinyUserInfo"]),
335            group=self.deserialize_clan(payload["group"]),
336        )

Deserialize a JSON payload of group information for a member.

Parameters
Returns
def deserialize_clan_conversations( self, payload: list[typing.Any]) -> collections.abc.Sequence[aiobungie.crates.clans.ClanConversation]:
354    def deserialize_clan_conversations(
355        self, payload: typedefs.JSONArray
356    ) -> collections.Sequence[clans.ClanConversation]:
357        return [self._deserialize_clan_conversation(conv) for conv in payload]

Deserialize a JSON array of a clan conversations information.

Parameters
Returns
def deserialize_app_owner( self, payload: dict[str, typing.Any]) -> aiobungie.crates.application.ApplicationOwner:
359    def deserialize_app_owner(
360        self, payload: typedefs.JSONObject
361    ) -> application.ApplicationOwner:
362        return application.ApplicationOwner(
363            net=self._net,
364            name=payload.get("bungieGlobalDisplayName", undefined.UNDEFINED),
365            id=int(payload["membershipId"]),
366            type=enums.MembershipType(payload["membershipType"]),
367            icon=assets.Image(str(payload["iconPath"])),
368            is_public=payload["isPublic"],
369            code=payload.get("bungieGlobalDisplayNameCode", None),
370        )

Deserialize a JSON payload of Bungie Developer portal application owner information.

Parameters
Returns
  • aiobungie.crates.application.ApplicationOwner: An application owner.
def deserialize_app( self, payload: dict[str, typing.Any]) -> aiobungie.crates.application.Application:
372    def deserialize_app(self, payload: typedefs.JSONObject) -> application.Application:
373        return application.Application(
374            id=int(payload["applicationId"]),
375            name=payload["name"],
376            link=payload["link"],
377            status=payload["status"],
378            redirect_url=payload.get("redirectUrl", None),
379            created_at=time.clean_date(str(payload["creationDate"])),
380            published_at=time.clean_date(str(payload["firstPublished"])),
381            owner=self.deserialize_app_owner(payload["team"][0]["user"]),  # type: ignore
382            scope=payload.get("scope", undefined.UNDEFINED),
383        )

Deserialize a JSON payload of Bungie Developer portal application information.

Parameters
Returns
  • aiobungie.crates.application.Application: An application.
def deserialize_profile( self, payload: dict[str, typing.Any], /) -> Optional[aiobungie.crates.profile.Profile]:
406    def deserialize_profile(
407        self, payload: typedefs.JSONObject, /
408    ) -> typing.Optional[profile.Profile]:
409        if (raw_profile := payload.get("data")) is None:
410            return None
411
412        payload = raw_profile
413        id = int(payload["userInfo"]["membershipId"])
414        name = payload["userInfo"]["displayName"]
415        is_public = payload["userInfo"]["isPublic"]
416        type = enums.MembershipType(payload["userInfo"]["membershipType"])
417        last_played = time.clean_date(str(payload["dateLastPlayed"]))
418        character_ids = [int(cid) for cid in payload["characterIds"]]
419        power_cap = payload["currentSeasonRewardPowerCap"]
420
421        return profile.Profile(
422            id=int(id),
423            name=name,
424            is_public=is_public,
425            type=type,
426            last_played=last_played,
427            character_ids=character_ids,
428            power_cap=power_cap,
429            net=self._net,
430        )

Deserialize a JSON payload of Bungie.net profile information.

Parameters
Returns
def deserialize_profile_item( self, payload: dict[str, typing.Any]) -> aiobungie.crates.profile.ProfileItemImpl:
432    def deserialize_profile_item(
433        self, payload: typedefs.JSONObject
434    ) -> profile.ProfileItemImpl:
435
436        instance_id: typing.Optional[int] = None
437        if raw_instance_id := payload.get("itemInstanceId"):
438            instance_id = int(raw_instance_id)
439
440        version_number: typing.Optional[int] = None
441        if raw_version := payload.get("versionNumber"):
442            version_number = int(raw_version)
443
444        transfer_status = enums.TransferStatus(payload["transferStatus"])
445
446        return profile.ProfileItemImpl(
447            net=self._net,
448            hash=payload["itemHash"],
449            quantity=payload["quantity"],
450            bind_status=enums.ItemBindStatus(payload["bindStatus"]),
451            location=enums.ItemLocation(payload["location"]),
452            bucket=payload["bucketHash"],
453            transfer_status=transfer_status,
454            lockable=payload["lockable"],
455            state=enums.ItemState(payload["state"]),
456            dismantel_permissions=payload["dismantlePermission"],
457            is_wrapper=payload["isWrapper"],
458            instance_id=instance_id,
459            version_number=version_number,
460            ornament_id=payload.get("overrideStyleItemHash"),
461        )

Deserialize a JSON payload of a singular profile component item.

Parameters
Returns
def deserialize_objectives( self, payload: dict[str, typing.Any]) -> aiobungie.crates.records.Objective:
463    def deserialize_objectives(self, payload: typedefs.JSONObject) -> records.Objective:
464        return records.Objective(
465            net=self._net,
466            hash=payload["objectiveHash"],
467            visible=payload["visible"],
468            complete=payload["complete"],
469            completion_value=payload["completionValue"],
470            progress=payload.get("progress"),
471            destination_hash=payload.get("destinationHash"),
472            activity_hash=payload.get("activityHash"),
473        )

Deserialize a JSON payload of an objective found in a record profile component.

Parameters
  • payload (aiobungie.internal.helpers.JsonObject): The JSON payload.
Returns
  • aiobungie.crates.records.Objective: A record objective object.
def deserialize_records( self, payload: dict[str, typing.Any], scores: Optional[aiobungie.crates.records.RecordScores] = None, **nodes: int) -> aiobungie.crates.records.Record:
475    def deserialize_records(
476        self,
477        payload: typedefs.JSONObject,
478        scores: typing.Optional[records.RecordScores] = None,
479        **nodes: int,
480    ) -> records.Record:
481        objectives: typing.Optional[list[records.Objective]] = None
482        interval_objectives: typing.Optional[list[records.Objective]] = None
483        record_state: typedefs.IntAnd[records.RecordState]
484
485        record_state = records.RecordState(payload["state"])
486
487        if raw_objs := payload.get("objectives"):
488            objectives = [self.deserialize_objectives(obj) for obj in raw_objs]
489
490        if raw_interval_objs := payload.get("intervalObjectives"):
491            interval_objectives = [
492                self.deserialize_objectives(obj) for obj in raw_interval_objs
493            ]
494
495        return records.Record(
496            scores=scores,
497            categories_node_hash=nodes.get("categories_hash", undefined.UNDEFINED),
498            seals_node_hash=nodes.get("seals_hash", undefined.UNDEFINED),
499            state=record_state,
500            objectives=objectives,
501            interval_objectives=interval_objectives,
502            redeemed_count=payload.get("intervalsRedeemedCount", 0),
503            completion_times=payload.get("completedCount", None),
504            reward_visibility=payload.get("rewardVisibilty", None),
505        )

Deserialize a JSON object of a profile record component.

Parameters
  • payload (aiobungie.internal.helpers.JsonObject): The JSON object payload
  • scores (typing.Optional[records.RecordScores]): The records scores object. This exists only to keep the signature of aiobungie.crates.CharacterRecord with the record object. As it will always be None in that object.
  • **nodes (int): An int kwargs use to grab the node hashes while deserializing components.
Returns
  • aiobungie.records.Record: A standard implementation of a profile record component.
def deserialize_character_records( self, payload: dict[str, typing.Any], scores: Optional[aiobungie.crates.records.RecordScores] = None, record_hashes: Optional[list[int]] = None) -> aiobungie.crates.records.CharacterRecord:
507    def deserialize_character_records(
508        self,
509        payload: typedefs.JSONObject,
510        scores: typing.Optional[records.RecordScores] = None,
511        record_hashes: typing.Optional[list[int]] = None,
512    ) -> records.CharacterRecord:
513
514        record = self.deserialize_records(payload, scores)
515        return records.CharacterRecord(
516            scores=scores,
517            categories_node_hash=record.categories_node_hash,
518            seals_node_hash=record.seals_node_hash,
519            state=record.state,
520            objectives=record.objectives,
521            interval_objectives=record.interval_objectives,
522            redeemed_count=payload.get("intervalsRedeemedCount", 0),
523            completion_times=payload.get("completedCount"),
524            reward_visibility=payload.get("rewardVisibilty"),
525            record_hashes=record_hashes or [],
526        )

Deserialize a JSON object of a profile character record component.

This almost does the same this as deserialize_records but has more fields which can only be found in a character record.

Parameters
  • payload (aiobungie.internal.helpers.JsonObject): The JSON object payload
Returns
  • aiobungie.records.CharacterRecord: A standard implementation of a profile character record component.
def deserialize_character_dye(self, payload: dict[str, typing.Any]) -> aiobungie.crates.character.Dye:
528    def deserialize_character_dye(self, payload: typedefs.JSONObject) -> character.Dye:
529        return character.Dye(
530            channel_hash=payload["channelHash"], dye_hash=payload["dyeHash"]
531        )

Deserialize a JSON payload of a character's dye information.

Parameters
Returns
  • aiobungie.crates.character.Dye: Information about a character dye object.
def deserialize_character_customization( self, payload: dict[str, typing.Any]) -> aiobungie.crates.character.CustomizationOptions:
533    def deserialize_character_customization(
534        self, payload: typedefs.JSONObject
535    ) -> character.CustomizationOptions:
536        return character.CustomizationOptions(
537            personality=payload["personality"],
538            face=payload["face"],
539            skin_color=payload["skinColor"],
540            lip_color=payload["lipColor"],
541            eye_color=payload["eyeColor"],
542            hair_colors=payload.get("hairColors", []),
543            feature_colors=payload.get("featureColors", []),
544            decal_color=payload["decalColor"],
545            wear_helmet=payload["wearHelmet"],
546            hair_index=payload["hairIndex"],
547            feature_index=payload["featureIndex"],
548            decal_index=payload["decalIndex"],
549        )

Deserialize a JSON payload of a character customization information found in character render data profile component.

Parameters
Returns
  • aiobungie.crates.character.CustomizationOptions: Information about a character customs object.
def deserialize_character_minimal_equipments( self, payload: dict[str, typing.Any]) -> aiobungie.crates.character.MinimalEquipments:
551    def deserialize_character_minimal_equipments(
552        self, payload: typedefs.JSONObject
553    ) -> character.MinimalEquipments:
554        dyes = None
555        if raw_dyes := payload.get("dyes"):
556            if raw_dyes:
557                dyes = [self.deserialize_character_dye(dye) for dye in raw_dyes]
558        return character.MinimalEquipments(
559            net=self._net, item_hash=payload["itemHash"], dyes=dyes
560        )

Deserialize a singular JSON peer view of equipment found in character render data profile component.

Parameters
Returns
  • aiobungie.crates.character.MinimalEquipments: A minimal equipment object.
def deserialize_character_render_data( self, payload: dict[str, typing.Any], /) -> aiobungie.crates.character.RenderedData:
562    def deserialize_character_render_data(
563        self, payload: typedefs.JSONObject, /
564    ) -> character.RenderedData:
565        return character.RenderedData(
566            net=self._net,
567            customization=self.deserialize_character_customization(
568                payload["customization"]
569            ),
570            custom_dyes=[
571                self.deserialize_character_dye(dye)
572                for dye in payload["customDyes"]
573                if dye
574            ],
575            equipment=[
576                self.deserialize_character_minimal_equipments(equipment)
577                for equipment in payload["peerView"]["equipment"]
578            ],
579        )

Deserialize a JSON payload of a profile character render data component.

Parameters
Returns
def deserialize_available_activity( self, payload: dict[str, typing.Any]) -> aiobungie.crates.activity.AvailableActivity:
581    def deserialize_available_activity(
582        self, payload: typedefs.JSONObject
583    ) -> activity.AvailableActivity:
584        return activity.AvailableActivity(
585            hash=payload["activityHash"],
586            is_new=payload["isNew"],
587            is_completed=payload["isCompleted"],
588            is_visible=payload["isVisible"],
589            display_level=payload.get("displayLevel"),
590            recommended_light=payload.get("recommendedLight"),
591            difficulty=activity.Difficulty(payload["difficultyTier"]),
592            can_join=payload["canJoin"],
593            can_lead=payload["canLead"],
594        )

Deserialize a JSON payload of an available activities.

This method is used to deserialize an array of aiobungie.crates.CharacterActivity.available_activities.

Parameters
Returns
def deserialize_character_activity( self, payload: dict[str, typing.Any]) -> aiobungie.crates.activity.CharacterActivity:
596    def deserialize_character_activity(
597        self, payload: typedefs.JSONObject
598    ) -> activity.CharacterActivity:
599        current_mode: typing.Optional[enums.GameMode] = None
600        if raw_current_mode := payload.get("currentActivityModeType"):
601            current_mode = enums.GameMode(raw_current_mode)
602
603        current_mode_types: typing.Optional[collections.Sequence[enums.GameMode]] = None
604        if raw_current_modes := payload.get("currentActivityModeTypes"):
605            current_mode_types = [enums.GameMode(type_) for type_ in raw_current_modes]
606
607        return activity.CharacterActivity(
608            date_started=time.clean_date(payload["dateActivityStarted"]),
609            current_hash=payload["currentActivityHash"],
610            current_mode_hash=payload["currentActivityModeHash"],
611            current_mode=current_mode,
612            current_mode_hashes=payload.get("currentActivityModeHashes"),
613            current_mode_types=current_mode_types,
614            current_playlist_hash=payload.get("currentPlaylistActivityHash"),
615            last_story_hash=payload["lastCompletedStoryHash"],
616            available_activities=[
617                self.deserialize_available_activity(activity_)
618                for activity_ in payload["availableActivities"]
619            ],
620        )

Deserialize a JSON payload of character activity profile component.

Parameters
Returns
def deserialize_profile_items( self, payload: dict[str, typing.Any], /) -> list[aiobungie.crates.profile.ProfileItemImpl]:
622    def deserialize_profile_items(
623        self, payload: typedefs.JSONObject, /
624    ) -> list[profile.ProfileItemImpl]:
625        return [self.deserialize_profile_item(item) for item in payload["items"]]

Deserialize a JSON payload of profile items component information.

This may deserialize profileInventories or profileCurrencies or any other alternatives.

Parameters
Returns
def deserialize_progressions( self, payload: dict[str, typing.Any]) -> aiobungie.crates.progressions.Progression:
668    def deserialize_progressions(
669        self, payload: typedefs.JSONObject
670    ) -> progressions.Progression:
671        return progressions.Progression(
672            hash=int(payload["progressionHash"]),
673            level=int(payload["level"]),
674            cap=int(payload["levelCap"]),
675            daily_limit=int(payload["dailyLimit"]),
676            weekly_limit=int(payload["weeklyLimit"]),
677            current_progress=int(payload["currentProgress"]),
678            daily_progress=int(payload["dailyProgress"]),
679            needed=int(payload["progressToNextLevel"]),
680            next_level=int(payload["nextLevelAt"]),
681        )
def deserialize_milestone( self, payload: dict[str, typing.Any]) -> aiobungie.crates.milestones.Milestone:
769    def deserialize_milestone(
770        self, payload: typedefs.JSONObject
771    ) -> milestones.Milestone:
772        start_date: typing.Optional[datetime.datetime] = None
773        if raw_start_date := payload.get("startDate"):
774            start_date = time.clean_date(raw_start_date)
775
776        end_date: typing.Optional[datetime.datetime] = None
777        if raw_end_date := payload.get("endDate"):
778            end_date = time.clean_date(raw_end_date)
779
780        rewards: typing.Optional[
781            collections.Collection[milestones.MilestoneReward]
782        ] = None
783        if raw_rewards := payload.get("rewards"):
784            rewards = [
785                self._deserialize_milestone_rewards(reward) for reward in raw_rewards
786            ]
787
788        activities: typing.Optional[
789            collections.Sequence[milestones.MilestoneActivity]
790        ] = None
791        if raw_activities := payload.get("activities"):
792            activities = [
793                self._deserialize_milestone_activity(active)
794                for active in raw_activities
795            ]
796
797        quests: typing.Optional[collections.Sequence[milestones.MilestoneQuest]] = None
798        if raw_quests := payload.get("availableQuests"):
799            quests = [
800                self._deserialize_milestone_available_quest(quest)
801                for quest in raw_quests
802            ]
803
804        vendors: typing.Optional[
805            collections.Sequence[milestones.MilestoneVendor]
806        ] = None
807        if raw_vendors := payload.get("vendors"):
808            vendors = [
809                milestones.MilestoneVendor(
810                    vendor_hash=vendor["vendorHash"],
811                    preview_itemhash=vendor.get("previewItemHash"),
812                )
813                for vendor in raw_vendors
814            ]
815
816        return milestones.Milestone(
817            hash=payload["milestoneHash"],
818            start_date=start_date,
819            end_date=end_date,
820            order=payload["order"],
821            rewards=rewards,
822            available_quests=quests,
823            activities=activities,
824            vendors=vendors,
825        )
def deserialize_characters( self, payload: dict[str, typing.Any]) -> collections.abc.Mapping[int, aiobungie.crates.character.Character]:
842    def deserialize_characters(
843        self, payload: typedefs.JSONObject
844    ) -> collections.Mapping[int, character.Character]:
845        return {
846            int(char_id): self._set_character_attrs(char)
847            for char_id, char in payload["data"].items()
848        }
def deserialize_character( self, payload: dict[str, typing.Any]) -> aiobungie.crates.character.Character:
850    def deserialize_character(
851        self, payload: typedefs.JSONObject
852    ) -> character.Character:
853        return self._set_character_attrs(payload)
def deserialize_character_equipments( self, payload: dict[str, typing.Any]) -> collections.abc.Mapping[int, collections.abc.Sequence[aiobungie.crates.profile.ProfileItemImpl]]:
855    def deserialize_character_equipments(
856        self, payload: typedefs.JSONObject
857    ) -> collections.Mapping[int, collections.Sequence[profile.ProfileItemImpl]]:
858        return {
859            int(char_id): self.deserialize_profile_items(item)
860            for char_id, item in payload["data"].items()
861        }
def deserialize_character_activities( self, payload: dict[str, typing.Any]) -> collections.abc.Mapping[int, aiobungie.crates.activity.CharacterActivity]:
863    def deserialize_character_activities(
864        self, payload: typedefs.JSONObject
865    ) -> collections.Mapping[int, activity.CharacterActivity]:
866        return {
867            int(char_id): self.deserialize_character_activity(data)
868            for char_id, data in payload["data"].items()
869        }
def deserialize_characters_render_data( self, payload: dict[str, typing.Any]) -> collections.abc.Mapping[int, aiobungie.crates.character.RenderedData]:
871    def deserialize_characters_render_data(
872        self, payload: typedefs.JSONObject
873    ) -> collections.Mapping[int, character.RenderedData]:
874        return {
875            int(char_id): self.deserialize_character_render_data(data)
876            for char_id, data in payload["data"].items()
877        }
def deserialize_character_progressions( self, payload: dict[str, typing.Any]) -> aiobungie.crates.character.CharacterProgression:
879    def deserialize_character_progressions(
880        self, payload: typedefs.JSONObject
881    ) -> character.CharacterProgression:
882        progressions_ = {
883            int(prog_id): self.deserialize_progressions(prog)
884            for prog_id, prog in payload["progressions"].items()
885        }
886
887        factions = {
888            int(faction_id): self._deserialize_factions(faction)
889            for faction_id, faction in payload["factions"].items()
890        }
891
892        milestones_ = {
893            int(milestone_hash): self.deserialize_milestone(milestone)
894            for milestone_hash, milestone in payload["milestones"].items()
895        }
896
897        uninstanced_item_objectives = {
898            int(item_hash): [self.deserialize_objectives(ins) for ins in obj]
899            for item_hash, obj in payload["uninstancedItemObjectives"].items()
900        }
901
902        artifact = payload["seasonalArtifact"]
903        seasonal_artifact = season.CharacterScopedArtifact(
904            hash=artifact["artifactHash"],
905            points_used=artifact["pointsUsed"],
906            reset_count=artifact["resetCount"],
907            tiers=[
908                self._deserialize_artifact_tiers(tier) for tier in artifact["tiers"]
909            ],
910        )
911        checklists = payload["checklists"]
912
913        return character.CharacterProgression(
914            progressions=progressions_,
915            factions=factions,
916            checklists=checklists,
917            milestones=milestones_,
918            seasonal_artifact=seasonal_artifact,
919            uninstanced_item_objectives=uninstanced_item_objectives,
920        )
def deserialize_character_progressions_mapping( self, payload: dict[str, typing.Any]) -> collections.abc.Mapping[int, aiobungie.crates.character.CharacterProgression]:
922    def deserialize_character_progressions_mapping(
923        self, payload: typedefs.JSONObject
924    ) -> collections.Mapping[int, character.CharacterProgression]:
925        character_progressions: collections.Mapping[
926            int, character.CharacterProgression
927        ] = {}
928        for char_id, data in payload["data"].items():
929            # A little hack to stop mypy complaining about Mapping <-> dict
930            character_progressions[int(char_id)] = self.deserialize_character_progressions(data)  # type: ignore[index]
931        return character_progressions
def deserialize_characters_records( self, payload: dict[str, typing.Any]) -> collections.abc.Mapping[int, aiobungie.crates.records.CharacterRecord]:
933    def deserialize_characters_records(
934        self,
935        payload: typedefs.JSONObject,
936    ) -> collections.Mapping[int, records.CharacterRecord]:
937
938        return {
939            int(rec_id): self.deserialize_character_records(
940                rec, record_hashes=payload.get("featuredRecordHashes")
941            )
942            for rec_id, rec in payload["records"].items()
943        }
def deserialize_profile_records( self, payload: dict[str, typing.Any]) -> collections.abc.Mapping[int, aiobungie.crates.records.Record]:
945    def deserialize_profile_records(
946        self, payload: typedefs.JSONObject
947    ) -> collections.Mapping[int, records.Record]:
948        raw_profile_records = payload["data"]
949        scores = records.RecordScores(
950            current_score=raw_profile_records["score"],
951            legacy_score=raw_profile_records["legacyScore"],
952            lifetime_score=raw_profile_records["lifetimeScore"],
953        )
954        return {
955            int(record_id): self.deserialize_records(
956                record,
957                scores,
958                categories_hash=raw_profile_records["recordCategoriesRootNodeHash"],
959                seals_hash=raw_profile_records["recordSealsRootNodeHash"],
960            )
961            for record_id, record in raw_profile_records["records"].items()
962        }
def deserialize_craftables_component( self, payload: dict[str, typing.Any]) -> aiobungie.crates.components.CraftablesComponent:
 999    def deserialize_craftables_component(
1000        self, payload: typedefs.JSONObject
1001    ) -> components.CraftablesComponent:
1002        return components.CraftablesComponent(
1003            net=self._net,
1004            craftables={
1005                int(item_id): self._deserialize_craftable_item(item)
1006                for item_id, item in payload["craftables"].items()
1007                if item is not None
1008            },
1009            crafting_root_node_hash=payload["craftingRootNodeHash"],
1010        )
def deserialize_components( self, payload: dict[str, typing.Any]) -> aiobungie.crates.components.Component:
1012    def deserialize_components(  # noqa: C901 Too complex.
1013        self, payload: typedefs.JSONObject
1014    ) -> components.Component:
1015
1016        profile_: typing.Optional[profile.Profile] = None
1017        if raw_profile := payload.get("profile"):
1018            profile_ = self.deserialize_profile(raw_profile)
1019
1020        profile_progression: typing.Optional[profile.ProfileProgression] = None
1021        if raw_profile_progression := payload.get("profileProgression"):
1022            profile_progression = self.deserialize_profile_progression(
1023                raw_profile_progression
1024            )
1025
1026        profile_currencies: typing.Optional[
1027            collections.Sequence[profile.ProfileItemImpl]
1028        ] = None
1029        if raw_profile_currencies := payload.get("profileCurrencies"):
1030            if "data" in raw_profile_currencies:
1031                profile_currencies = self.deserialize_profile_items(
1032                    raw_profile_currencies["data"]
1033                )
1034
1035        profile_inventories: typing.Optional[
1036            collections.Sequence[profile.ProfileItemImpl]
1037        ] = None
1038        if raw_profile_inventories := payload.get("profileInventory"):
1039            if "data" in raw_profile_inventories:
1040                profile_inventories = self.deserialize_profile_items(
1041                    raw_profile_inventories["data"]
1042                )
1043
1044        profile_records: typing.Optional[
1045            collections.Mapping[int, records.Record]
1046        ] = None
1047
1048        if raw_profile_records_ := payload.get("profileRecords"):
1049            profile_records = self.deserialize_profile_records(raw_profile_records_)
1050
1051        characters: typing.Optional[typing.Mapping[int, character.Character]] = None
1052        if raw_characters := payload.get("characters"):
1053            characters = self.deserialize_characters(raw_characters)
1054
1055        character_records: typing.Optional[
1056            collections.Mapping[int, records.CharacterRecord]
1057        ] = None
1058
1059        if raw_character_records := payload.get("characterRecords"):
1060            # Had to do it in two steps..
1061            to_update: typedefs.JSONObject = {}
1062            for _, data in raw_character_records["data"].items():
1063                for record_id, record in data.items():
1064                    to_update[record_id] = record
1065
1066            character_records = {
1067                int(rec_id): self.deserialize_character_records(
1068                    rec, record_hashes=to_update.get("featuredRecordHashes")
1069                )
1070                for rec_id, rec in to_update["records"].items()
1071            }
1072
1073        character_equipments: typing.Optional[
1074            collections.Mapping[int, collections.Sequence[profile.ProfileItemImpl]]
1075        ] = None
1076        if raw_character_equips := payload.get("characterEquipment"):
1077            character_equipments = self.deserialize_character_equipments(
1078                raw_character_equips
1079            )
1080
1081        character_inventories: typing.Optional[
1082            collections.Mapping[int, collections.Sequence[profile.ProfileItemImpl]]
1083        ] = None
1084        if raw_character_inventories := payload.get("characterInventories"):
1085            if "data" in raw_character_inventories:
1086                character_inventories = self.deserialize_character_equipments(
1087                    raw_character_inventories
1088                )
1089
1090        character_activities: typing.Optional[
1091            collections.Mapping[int, activity.CharacterActivity]
1092        ] = None
1093        if raw_char_acts := payload.get("characterActivities"):
1094            character_activities = self.deserialize_character_activities(raw_char_acts)
1095
1096        character_render_data: typing.Optional[
1097            collections.Mapping[int, character.RenderedData]
1098        ] = None
1099        if raw_character_render_data := payload.get("characterRenderData"):
1100            character_render_data = self.deserialize_characters_render_data(
1101                raw_character_render_data
1102            )
1103
1104        character_progressions: typing.Optional[
1105            collections.Mapping[int, character.CharacterProgression]
1106        ] = None
1107
1108        if raw_character_progressions := payload.get("characterProgressions"):
1109            character_progressions = self.deserialize_character_progressions_mapping(
1110                raw_character_progressions
1111            )
1112
1113        profile_string_vars: typing.Optional[collections.Mapping[int, int]] = None
1114        if raw_profile_string_vars := payload.get("profileStringVariables"):
1115            profile_string_vars = raw_profile_string_vars["data"]["integerValuesByHash"]
1116
1117        character_string_vars: typing.Optional[
1118            collections.Mapping[int, collections.Mapping[int, int]]
1119        ] = None
1120        if raw_character_string_vars := payload.get("characterStringVariables"):
1121            character_string_vars = {
1122                int(char_id): data["integerValuesByHash"]
1123                for char_id, data in raw_character_string_vars["data"].items()
1124            }
1125
1126        metrics: typing.Optional[
1127            collections.Sequence[
1128                collections.Mapping[
1129                    int, tuple[bool, typing.Optional[records.Objective]]
1130                ]
1131            ]
1132        ] = None
1133        root_node_hash: typing.Optional[int] = None
1134
1135        if raw_metrics := payload.get("metrics"):
1136            root_node_hash = raw_metrics["data"]["metricsRootNodeHash"]
1137            metrics = [
1138                {
1139                    int(metrics_hash): (
1140                        data["invisible"],
1141                        self.deserialize_objectives(data["objectiveProgress"])
1142                        if "objectiveProgress" in data
1143                        else None,
1144                    )
1145                    for metrics_hash, data in raw_metrics["data"]["metrics"].items()
1146                }
1147            ]
1148        transitory: typing.Optional[fireteams.FireteamParty] = None
1149        if raw_transitory := payload.get("profileTransitoryData"):
1150            if "data" in raw_transitory:
1151                transitory = self.deserialize_fireteam_party(raw_transitory["data"])
1152
1153        item_components: typing.Optional[components.ItemsComponent] = None
1154        if raw_item_components := payload.get("itemComponents"):
1155            item_components = self.deserialize_items_component(raw_item_components)
1156
1157        profile_plugsets: typing.Optional[
1158            collections.Mapping[int, collections.Sequence[items.PlugItemState]]
1159        ] = None
1160
1161        if raw_profile_plugs := payload.get("profilePlugSets"):
1162            profile_plugsets = {
1163                int(index): [self.deserialize_plug_item_state(state) for state in data]
1164                for index, data in raw_profile_plugs["data"]["plugs"].items()
1165            }
1166
1167        character_plugsets: typing.Optional[
1168            collections.Mapping[
1169                int, collections.Mapping[int, collections.Sequence[items.PlugItemState]]
1170            ]
1171        ] = None
1172        if raw_char_plugsets := payload.get("characterPlugSets"):
1173            character_plugsets = {
1174                int(char_id): {
1175                    int(index): [
1176                        self.deserialize_plug_item_state(state) for state in data
1177                    ]
1178                    for index, data in inner["plugs"].items()
1179                }
1180                for char_id, inner in raw_char_plugsets["data"].items()
1181            }
1182
1183        character_collectibles: typing.Optional[
1184            collections.Mapping[int, items.Collectible]
1185        ] = None
1186        if raw_character_collectibles := payload.get("characterCollectibles"):
1187            character_collectibles = {
1188                int(char_id): self._deserialize_collectible(data)
1189                for char_id, data in raw_character_collectibles["data"].items()
1190            }
1191
1192        profile_collectibles: typing.Optional[items.Collectible] = None
1193        if raw_profile_collectibles := payload.get("profileCollectibles"):
1194            profile_collectibles = self._deserialize_collectible(
1195                raw_profile_collectibles["data"]
1196            )
1197
1198        profile_nodes: typing.Optional[collections.Mapping[int, records.Node]] = None
1199        if raw_profile_nodes := payload.get("profilePresentationNodes"):
1200            profile_nodes = {
1201                int(node_hash): self._deserialize_node(node)
1202                for node_hash, node in raw_profile_nodes["data"]["nodes"].items()
1203            }
1204
1205        character_nodes: typing.Optional[
1206            collections.Mapping[int, collections.Mapping[int, records.Node]]
1207        ] = None
1208        if raw_character_nodes := payload.get("characterPresentationNodes"):
1209            character_nodes = {
1210                int(char_id): {
1211                    int(node_hash): self._deserialize_node(node)
1212                    for node_hash, node in each_character["nodes"].items()
1213                }
1214                for char_id, each_character in raw_character_nodes["data"].items()
1215            }
1216
1217        platform_silver: typing.Optional[
1218            collections.Mapping[str, profile.ProfileItemImpl]
1219        ] = None
1220        if raw_platform_silver := payload.get("platformSilver"):
1221            if "data" in raw_platform_silver:
1222                platform_silver = {
1223                    platform_name: self.deserialize_profile_item(item)
1224                    for platform_name, item in raw_platform_silver["data"][
1225                        "platformSilver"
1226                    ].items()
1227                }
1228
1229        character_currency_lookups: typing.Optional[
1230            collections.Mapping[int, collections.Sequence[items.Currency]]
1231        ] = None
1232        if raw_char_lookups := payload.get("characterCurrencyLookups"):
1233            if "data" in raw_char_lookups:
1234                character_currency_lookups = {
1235                    int(char_id): self._deserialize_currencies(currencie)
1236                    for char_id, currencie in raw_char_lookups["data"].items()
1237                }
1238
1239        character_craftables: typing.Optional[
1240            collections.Mapping[int, components.CraftablesComponent]
1241        ] = None
1242        if raw_character_craftables := payload.get("characterCraftables"):
1243
1244            if "data" in raw_character_craftables:
1245                character_craftables = {
1246                    int(char_id): self.deserialize_craftables_component(craftable)
1247                    for char_id, craftable in raw_character_craftables["data"].items()
1248                }
1249
1250        return components.Component(
1251            profiles=profile_,
1252            profile_progression=profile_progression,
1253            profile_currencies=profile_currencies,
1254            profile_inventories=profile_inventories,
1255            profile_records=profile_records,
1256            characters=characters,
1257            character_records=character_records,
1258            character_equipments=character_equipments,
1259            character_inventories=character_inventories,
1260            character_activities=character_activities,
1261            character_render_data=character_render_data,
1262            character_progressions=character_progressions,
1263            profile_string_variables=profile_string_vars,
1264            character_string_variables=character_string_vars,
1265            metrics=metrics,
1266            root_node_hash=root_node_hash,
1267            transitory=transitory,
1268            item_components=item_components,
1269            profile_plugsets=profile_plugsets,
1270            character_plugsets=character_plugsets,
1271            character_collectibles=character_collectibles,
1272            profile_collectibles=profile_collectibles,
1273            profile_nodes=profile_nodes,
1274            character_nodes=character_nodes,
1275            platform_silver=platform_silver,
1276            character_currency_lookups=character_currency_lookups,
1277            character_craftables=character_craftables,
1278        )

Deserialize a JSON payload of Bungie.net profile components information.

Parameters
  • payload (aiobungie.internal.helpers.JsonObject): The JSON payload.
Returns
def deserialize_items_component( self, payload: dict[str, typing.Any]) -> aiobungie.crates.components.ItemsComponent:
1280    def deserialize_items_component(
1281        self, payload: typedefs.JSONObject
1282    ) -> components.ItemsComponent:
1283        instances: typing.Optional[
1284            collections.Sequence[collections.Mapping[int, items.ItemInstance]]
1285        ] = None
1286        if raw_instances := payload.get("instances"):
1287            instances = [
1288                {
1289                    int(ins_id): self.deserialize_instanced_item(item)
1290                    for ins_id, item in raw_instances["data"].items()
1291                }
1292            ]
1293
1294        render_data: typing.Optional[
1295            collections.Mapping[int, tuple[bool, dict[int, int]]]
1296        ] = None
1297        if raw_render_data := payload.get("renderData"):
1298            render_data = {
1299                int(ins_id): (data["useCustomDyes"], data["artRegions"])
1300                for ins_id, data in raw_render_data["data"].items()
1301            }
1302
1303        stats: typing.Optional[collections.Mapping[int, items.ItemStatsView]] = None
1304        if raw_stats := payload.get("stats"):
1305            builder: collections.Mapping[int, items.ItemStatsView] = {}
1306            for ins_id, stat in raw_stats["data"].items():
1307                for _, items_ in stat.items():
1308                    builder[int(ins_id)] = self.deserialize_item_stats_view(items_)  # type: ignore[index]
1309            stats = builder
1310
1311        sockets: typing.Optional[
1312            collections.Mapping[int, collections.Sequence[items.ItemSocket]]
1313        ] = None
1314        if raw_sockets := payload.get("sockets"):
1315            sockets = {
1316                int(ins_id): [
1317                    self.deserialize_item_socket(socket) for socket in item["sockets"]
1318                ]
1319                for ins_id, item in raw_sockets["data"].items()
1320            }
1321
1322        objeectives: typing.Optional[
1323            collections.Mapping[int, collections.Sequence[records.Objective]]
1324        ] = None
1325        if raw_objectives := payload.get("objectives"):
1326            objeectives = {
1327                int(ins_id): [self.deserialize_objectives(objective)]
1328                for ins_id, data in raw_objectives["data"].items()
1329                for objective in data["objectives"]
1330            }
1331
1332        perks: typing.Optional[
1333            collections.Mapping[int, collections.Collection[items.ItemPerk]]
1334        ] = None
1335        if raw_perks := payload.get("perks"):
1336            perks = {
1337                int(ins_id): [
1338                    self.deserialize_item_perk(perk) for perk in item["perks"]
1339                ]
1340                for ins_id, item in raw_perks["data"].items()
1341            }
1342
1343        plug_states: typing.Optional[collections.Sequence[items.PlugItemState]] = None
1344        if raw_plug_states := payload.get("plugStates"):
1345            pending_states: list[items.PlugItemState] = []
1346            for _, plug in raw_plug_states["data"].items():
1347                pending_states.append(self.deserialize_plug_item_state(plug))
1348            plug_states = pending_states
1349
1350        reusable_plugs: typing.Optional[
1351            collections.Mapping[int, collections.Sequence[items.PlugItemState]]
1352        ] = None
1353        if raw_re_plugs := payload.get("reusablePlugs"):
1354            reusable_plugs = {
1355                int(ins_id): [
1356                    self.deserialize_plug_item_state(state) for state in inner
1357                ]
1358                for ins_id, plug in raw_re_plugs["data"].items()
1359                for inner in list(plug["plugs"].values())
1360            }
1361
1362        plug_objectives: typing.Optional[
1363            collections.Mapping[
1364                int, collections.Mapping[int, collections.Collection[records.Objective]]
1365            ]
1366        ] = None
1367        if raw_plug_objectives := payload.get("plugObjectives"):
1368            plug_objectives = {
1369                int(ins_id): {
1370                    int(obj_hash): [self.deserialize_objectives(obj) for obj in objs]
1371                    for obj_hash, objs in inner["objectivesPerPlug"].items()
1372                }
1373                for ins_id, inner in raw_plug_objectives["data"].items()
1374            }
1375
1376        return components.ItemsComponent(
1377            sockets=sockets,
1378            stats=stats,
1379            render_data=render_data,
1380            instances=instances,
1381            objectives=objeectives,
1382            perks=perks,
1383            plug_states=plug_states,
1384            reusable_plugs=reusable_plugs,
1385            plug_objectives=plug_objectives,
1386        )

Deserialize a JSON objects within the itemComponents key.`

def deserialize_character_component( self, payload: dict[str, typing.Any]) -> aiobungie.crates.components.CharacterComponent:
1388    def deserialize_character_component(  # type: ignore[call-arg]
1389        self, payload: typedefs.JSONObject
1390    ) -> components.CharacterComponent:
1391
1392        character_: typing.Optional[character.Character] = None
1393        if raw_singuler_character := payload.get("character"):
1394            character_ = self.deserialize_character(raw_singuler_character["data"])
1395
1396        inventory: typing.Optional[collections.Sequence[profile.ProfileItemImpl]] = None
1397        if raw_inventory := payload.get("inventory"):
1398            if "data" in raw_inventory:
1399                inventory = self.deserialize_profile_items(raw_inventory["data"])
1400
1401        activities: typing.Optional[activity.CharacterActivity] = None
1402        if raw_activities := payload.get("activities"):
1403            activities = self.deserialize_character_activity(raw_activities["data"])
1404
1405        equipment: typing.Optional[collections.Sequence[profile.ProfileItemImpl]] = None
1406        if raw_equipments := payload.get("equipment"):
1407            equipment = self.deserialize_profile_items(raw_equipments["data"])
1408
1409        progressions_: typing.Optional[character.CharacterProgression] = None
1410        if raw_progressions := payload.get("progressions"):
1411            progressions_ = self.deserialize_character_progressions(
1412                raw_progressions["data"]
1413            )
1414
1415        render_data: typing.Optional[character.RenderedData] = None
1416        if raw_render_data := payload.get("renderData"):
1417            render_data = self.deserialize_character_render_data(
1418                raw_render_data["data"]
1419            )
1420
1421        character_records: typing.Optional[
1422            collections.Mapping[int, records.CharacterRecord]
1423        ] = None
1424        if raw_char_records := payload.get("records"):
1425            character_records = self.deserialize_characters_records(
1426                raw_char_records["data"]
1427            )
1428
1429        item_components: typing.Optional[components.ItemsComponent] = None
1430        if raw_item_components := payload.get("itemComponents"):
1431            item_components = self.deserialize_items_component(raw_item_components)
1432
1433        nodes: typing.Optional[collections.Mapping[int, records.Node]] = None
1434        if raw_nodes := payload.get("presentationNodes"):
1435            nodes = {
1436                int(node_hash): self._deserialize_node(node)
1437                for node_hash, node in raw_nodes["data"]["nodes"].items()
1438            }
1439
1440        collectibles: typing.Optional[items.Collectible] = None
1441        if raw_collectibles := payload.get("collectibles"):
1442            collectibles = self._deserialize_collectible(raw_collectibles["data"])
1443
1444        currency_lookups: typing.Optional[collections.Sequence[items.Currency]] = None
1445        if raw_currencies := payload.get("currencyLookups"):
1446            if "data" in raw_currencies:
1447                currency_lookups = self._deserialize_currencies(raw_currencies)
1448
1449        return components.CharacterComponent(
1450            activities=activities,
1451            equipment=equipment,
1452            inventory=inventory,
1453            progressions=progressions_,
1454            render_data=render_data,
1455            character=character_,
1456            character_records=character_records,
1457            profile_records=None,
1458            item_components=item_components,
1459            currency_lookups=currency_lookups,
1460            collectibles=collectibles,
1461            nodes=nodes,
1462        )

Deserialize a JSON payload of Destiny 2 character component.

Parameters
Returns
def deserialize_inventory_results( self, payload: dict[str, typing.Any]) -> aiobungie.Iterator[aiobungie.crates.entity.SearchableEntity]:
1490    def deserialize_inventory_results(
1491        self, payload: typedefs.JSONObject
1492    ) -> iterators.Iterator[entity.SearchableEntity]:
1493        suggested_words: list[str] = payload["suggestedWords"]
1494
1495        def _check_unknown(s: str) -> undefined.UndefinedOr[str]:
1496            return s if not typedefs.is_unknown(s) else undefined.UNDEFINED
1497
1498        return iterators.Iterator(
1499            [
1500                entity.SearchableEntity(
1501                    net=self._net,
1502                    hash=data["hash"],
1503                    entity_type=data["entityType"],
1504                    weight=data["weight"],
1505                    suggested_words=suggested_words,
1506                    name=data["displayProperties"]["name"],
1507                    has_icon=data["displayProperties"]["hasIcon"],
1508                    description=_check_unknown(
1509                        data["displayProperties"]["description"]
1510                    ),
1511                    icon=assets.Image(data["displayProperties"]["icon"]),
1512                )
1513                for data in payload["results"]["results"]
1514            ]
1515        )

Deserialize results of searched Destiny2 entities.

Parameters
Returns
def deserialize_inventory_entity( self, payload: dict[str, typing.Any], /) -> aiobungie.crates.entity.InventoryEntity:
1544    def deserialize_inventory_entity(  # noqa: C901 Too complex.
1545        self, payload: typedefs.JSONObject, /
1546    ) -> entity.InventoryEntity:
1547
1548        props = self._set_entity_attrs(payload)
1549        objects = self._deserialize_inventory_item_objects(payload)
1550
1551        collectible_hash: typing.Optional[int] = None
1552        if raw_collectible_hash := payload.get("collectibleHash"):
1553            collectible_hash = int(raw_collectible_hash)
1554
1555        secondary_icon: undefined.UndefinedOr[assets.Image] = undefined.UNDEFINED
1556        if raw_second_icon := payload.get("secondaryIcon"):
1557            secondary_icon = assets.Image(raw_second_icon)
1558
1559        secondary_overlay: undefined.UndefinedOr[assets.Image] = undefined.UNDEFINED
1560        if raw_second_overlay := payload.get("secondaryOverlay"):
1561            secondary_overlay = assets.Image(raw_second_overlay)
1562
1563        secondary_special: undefined.UndefinedOr[assets.Image] = undefined.UNDEFINED
1564        if raw_second_special := payload.get("secondarySpecial"):
1565            secondary_special = assets.Image(raw_second_special)
1566
1567        screenshot: undefined.UndefinedOr[assets.Image] = undefined.UNDEFINED
1568        if raw_screenshot := payload.get("screenshot"):
1569            screenshot = assets.Image(raw_screenshot)
1570
1571        watermark_icon: typing.Optional[assets.Image] = None
1572        if raw_watermark_icon := payload.get("iconWatermark"):
1573            watermark_icon = assets.Image(raw_watermark_icon)
1574
1575        watermark_shelved: typing.Optional[assets.Image] = None
1576        if raw_watermark_shelved := payload.get("iconWatermarkShelved"):
1577            watermark_shelved = assets.Image(raw_watermark_shelved)
1578
1579        about: undefined.UndefinedOr[str] = undefined.UNDEFINED
1580        if (raw_about := payload.get("flavorText")) and not typedefs.is_unknown(
1581            raw_about
1582        ):
1583            about = raw_about
1584
1585        ui_item_style: undefined.UndefinedOr[str] = undefined.UNDEFINED
1586        if (
1587            raw_ui_style := payload.get("uiItemDisplayStyle")
1588        ) and not typedefs.is_unknown(raw_ui_style):
1589            ui_item_style = raw_ui_style
1590
1591        tier_and_name: undefined.UndefinedOr[str] = undefined.UNDEFINED
1592        if (
1593            raw_tier_and_name := payload.get("itemTypeAndTierDisplayName")
1594        ) and not typedefs.is_unknown(raw_tier_and_name):
1595            tier_and_name = raw_tier_and_name
1596
1597        type_name: undefined.UndefinedOr[str] = undefined.UNDEFINED
1598        if (
1599            raw_type_name := payload.get("itemTypeDisplayName")
1600        ) and not typedefs.is_unknown(raw_type_name):
1601            type_name = raw_type_name
1602
1603        display_source: undefined.UndefinedOr[str] = undefined.UNDEFINED
1604        if (
1605            raw_display_source := payload.get("displaySource")
1606        ) and not typedefs.is_unknown(raw_display_source):
1607            display_source = raw_display_source
1608
1609        lorehash: typing.Optional[int] = None
1610        if raw_lore_hash := payload.get("loreHash"):
1611            lorehash = int(raw_lore_hash)
1612
1613        summary_hash: typing.Optional[int] = None
1614        if raw_summary_hash := payload.get("summaryItemHash"):
1615            summary_hash = raw_summary_hash
1616
1617        breaker_type_hash: typing.Optional[int] = None
1618        if raw_breaker_type_hash := payload.get("breakerTypeHash"):
1619            breaker_type_hash = int(raw_breaker_type_hash)
1620
1621        damage_types: typing.Optional[collections.Sequence[int]] = None
1622        if raw_damage_types := payload.get("damageTypes"):
1623            damage_types = [int(type_) for type_ in raw_damage_types]
1624
1625        damagetype_hashes: typing.Optional[collections.Sequence[int]] = None
1626        if raw_damagetype_hashes := payload.get("damageTypeHashes"):
1627            damagetype_hashes = [int(type_) for type_ in raw_damagetype_hashes]
1628
1629        default_damagetype_hash: typing.Optional[int] = None
1630        if raw_defaultdmg_hash := payload.get("defaultDamageTypeHash"):
1631            default_damagetype_hash = int(raw_defaultdmg_hash)
1632
1633        emblem_objective_hash: typing.Optional[int] = None
1634        if raw_emblem_obj_hash := payload.get("emblemObjectiveHash"):
1635            emblem_objective_hash = int(raw_emblem_obj_hash)
1636
1637        tier_type: typing.Optional[enums.TierType] = None
1638        tier: typing.Optional[enums.ItemTier] = None
1639        bucket_hash: typing.Optional[int] = None
1640        recovery_hash: typing.Optional[int] = None
1641        tier_name: undefined.UndefinedOr[str] = undefined.UNDEFINED
1642        isinstance_item: bool = False
1643        expire_tool_tip: undefined.UndefinedOr[str] = undefined.UNDEFINED
1644        expire_in_orbit_message: undefined.UndefinedOr[str] = undefined.UNDEFINED
1645        suppress_expiration: bool = False
1646        max_stack_size: typing.Optional[int] = None
1647        stack_label: undefined.UndefinedOr[str] = undefined.UNDEFINED
1648
1649        if inventory := payload.get("inventory"):
1650            tier_type = enums.TierType(int(inventory["tierType"]))
1651            tier = enums.ItemTier(int(inventory["tierTypeHash"]))
1652            bucket_hash = int(inventory["bucketTypeHash"])
1653            recovery_hash = int(inventory["recoveryBucketTypeHash"])
1654            tier_name = inventory["tierTypeName"]
1655            isinstance_item = inventory["isInstanceItem"]
1656            suppress_expiration = inventory["suppressExpirationWhenObjectivesComplete"]
1657            max_stack_size = int(inventory["maxStackSize"])
1658
1659            try:
1660                stack_label = inventory["stackUniqueLabel"]
1661            except KeyError:
1662                pass
1663
1664        return entity.InventoryEntity(
1665            net=self._net,
1666            collectible_hash=collectible_hash,
1667            name=props.name,
1668            about=about,
1669            emblem_objective_hash=emblem_objective_hash,
1670            suppress_expiration=suppress_expiration,
1671            max_stack_size=max_stack_size,
1672            stack_label=stack_label,
1673            tier=tier,
1674            tier_type=tier_type,
1675            tier_name=tier_name,
1676            bucket_hash=bucket_hash,
1677            recovery_bucket_hash=recovery_hash,
1678            isinstance_item=isinstance_item,
1679            expire_in_orbit_message=expire_in_orbit_message,
1680            expiration_tooltip=expire_tool_tip,
1681            lore_hash=lorehash,
1682            type_and_tier_name=tier_and_name,
1683            summary_hash=summary_hash,
1684            ui_display_style=ui_item_style,
1685            type_name=type_name,
1686            breaker_type_hash=breaker_type_hash,
1687            description=props.description,
1688            display_source=display_source,
1689            hash=props.hash,
1690            damage_types=damage_types,
1691            index=props.index,
1692            icon=props.icon,
1693            has_icon=props.has_icon,
1694            screenshot=screenshot,
1695            watermark_icon=watermark_icon,
1696            watermark_shelved=watermark_shelved,
1697            secondary_icon=secondary_icon,
1698            secondary_overlay=secondary_overlay,
1699            secondary_special=secondary_special,
1700            type=enums.ItemType(int(payload["itemType"])),
1701            trait_hashes=[int(id_) for id_ in payload.get("traitHashes", [])],
1702            trait_ids=[trait for trait in payload.get("traitIds", [])],
1703            category_hashes=[int(hash_) for hash_ in payload["itemCategoryHashes"]],
1704            item_class=enums.Class(int(payload["classType"])),
1705            sub_type=enums.ItemSubType(int(payload["itemSubType"])),
1706            breaker_type=int(payload["breakerType"]),
1707            default_damagetype=int(payload["defaultDamageType"]),
1708            default_damagetype_hash=default_damagetype_hash,
1709            damagetype_hashes=damagetype_hashes,
1710            tooltip_notifications=payload["tooltipNotifications"],
1711            not_transferable=payload["nonTransferrable"],
1712            allow_actions=payload["allowActions"],
1713            is_equippable=payload["equippable"],
1714            objects=objects,
1715            background_colors=payload.get("backgroundColor", {}),
1716            season_hash=payload.get("seasonHash"),
1717            has_postmaster_effect=payload["doesPostmasterPullHaveSideEffects"],
1718        )

Deserialize a JSON payload of an inventory entity item information.

This can be any item from DestinyInventoryItemDefinition definition.

Parameters
Returns
def deserialize_objective_entity( self, payload: dict[str, typing.Any], /) -> aiobungie.crates.entity.ObjectiveEntity:
1720    def deserialize_objective_entity(
1721        self, payload: typedefs.JSONObject, /
1722    ) -> entity.ObjectiveEntity:
1723        props = self._set_entity_attrs(payload)
1724        return entity.ObjectiveEntity(
1725            net=self._net,
1726            hash=props.hash,
1727            index=props.index,
1728            description=props.description,
1729            name=props.name,
1730            has_icon=props.has_icon,
1731            icon=props.icon,
1732            unlock_value_hash=payload["unlockValueHash"],
1733            completion_value=payload["completionValue"],
1734            scope=entity.GatingScope(int(payload["scope"])),
1735            location_hash=payload["locationHash"],
1736            allowed_negative_value=payload["allowNegativeValue"],
1737            allowed_value_change=payload["allowValueChangeWhenCompleted"],
1738            counting_downward=payload["isCountingDownward"],
1739            value_style=entity.ValueUIStyle(int(payload["valueStyle"])),
1740            progress_description=payload["progressDescription"],
1741            perks=payload["perks"],
1742            stats=payload["stats"],
1743            minimum_visibility=payload["minimumVisibilityThreshold"],
1744            allow_over_completion=payload["allowOvercompletion"],
1745            show_value_style=payload["showValueOnComplete"],
1746            display_only_objective=payload["isDisplayOnlyObjective"],
1747            complete_value_style=entity.ValueUIStyle(
1748                int(payload["completedValueStyle"])
1749            ),
1750            progress_value_style=entity.ValueUIStyle(
1751                int(payload["inProgressValueStyle"])
1752            ),
1753            ui_label=payload["uiLabel"],
1754            ui_style=entity.ObjectiveUIStyle(int(payload["uiStyle"])),
1755        )

Deserialize a JSON payload of an objective entity information.

Parameters
Returns
def deserialize_activity( self, payload: dict[str, typing.Any], /) -> aiobungie.crates.activity.Activity:
1783    def deserialize_activity(
1784        self,
1785        payload: typedefs.JSONObject,
1786        /,
1787    ) -> activity.Activity:
1788        period = time.clean_date(payload["period"])
1789        details = payload["activityDetails"]
1790        ref_id = int(details["referenceId"])
1791        instance_id = int(details["instanceId"])
1792        mode = enums.GameMode(details["mode"])
1793        modes = [enums.GameMode(int(mode_)) for mode_ in details["modes"]]
1794        is_private = details["isPrivate"]
1795        membership_type = enums.MembershipType(int(details["membershipType"]))
1796
1797        # Since we're using the same fields for post activity method
1798        # this check is required since post activity doesn't values values
1799        values = self._deserialize_activity_values(payload["values"])
1800
1801        return activity.Activity(
1802            net=self._net,
1803            hash=ref_id,
1804            instance_id=instance_id,
1805            mode=mode,
1806            modes=modes,
1807            is_private=is_private,
1808            membership_type=membership_type,
1809            occurred_at=period,
1810            values=values,
1811        )

Deserialize a JSON payload of an activity history information.

Parameters
Returns
def deserialize_activities( self, payload: dict[str, typing.Any]) -> aiobungie.Iterator[aiobungie.crates.activity.Activity]:
1813    def deserialize_activities(
1814        self, payload: typedefs.JSONObject
1815    ) -> iterators.Iterator[activity.Activity]:
1816        return iterators.Iterator(
1817            [
1818                self.deserialize_activity(activity_)
1819                for activity_ in payload["activities"]
1820            ]
1821        )

Deserialize a JSON payload of an array of activity history information.

Parameters
Returns
def deserialize_extended_weapon_values( self, payload: dict[str, typing.Any]) -> aiobungie.crates.activity.ExtendedWeaponValues:
1823    def deserialize_extended_weapon_values(
1824        self, payload: typedefs.JSONObject
1825    ) -> activity.ExtendedWeaponValues:
1826
1827        assists: typing.Optional[int] = None
1828        if raw_assists := payload["values"].get("uniqueWeaponAssists"):
1829            assists = raw_assists["basic"]["value"]
1830        assists_damage: typing.Optional[int] = None
1831
1832        if raw_assists_damage := payload["values"].get("uniqueWeaponAssistDamage"):
1833            assists_damage = raw_assists_damage["basic"]["value"]
1834
1835        return activity.ExtendedWeaponValues(
1836            reference_id=int(payload["referenceId"]),
1837            kills=payload["values"]["uniqueWeaponKills"]["basic"]["value"],
1838            precision_kills=payload["values"]["uniqueWeaponPrecisionKills"]["basic"][
1839                "value"
1840            ],
1841            assists=assists,
1842            assists_damage=assists_damage,
1843            precision_kills_percentage=(
1844                payload["values"]["uniqueWeaponKillsPrecisionKills"]["basic"]["value"],
1845                payload["values"]["uniqueWeaponKillsPrecisionKills"]["basic"][
1846                    "displayValue"
1847                ],
1848            ),
1849        )

Deserialize values of extended weapons JSON object.

Parameters
Returns
def deserialize_post_activity_player( self, payload: dict[str, typing.Any], /) -> aiobungie.crates.activity.PostActivityPlayer:
1872    def deserialize_post_activity_player(
1873        self, payload: typedefs.JSONObject, /
1874    ) -> activity.PostActivityPlayer:
1875        player = payload["player"]
1876
1877        class_hash: typedefs.NoneOr[int] = None
1878        if (class_hash := player.get("classHash")) is not None:
1879            class_hash = class_hash
1880
1881        race_hash: typedefs.NoneOr[int] = None
1882        if (race_hash := player.get("raceHash")) is not None:
1883            race_hash = race_hash
1884
1885        gender_hash: typedefs.NoneOr[int] = None
1886        if (gender_hash := player.get("genderHash")) is not None:
1887            gender_hash = gender_hash
1888
1889        character_class: undefined.UndefinedOr[str] = undefined.UNDEFINED
1890        if (
1891            character_class := player.get("characterClass")
1892        ) and not typedefs.is_unknown(character_class):
1893            character_class = character_class
1894
1895        character_level: typedefs.NoneOr[int] = None
1896        if (character_level := player.get("characterLevel")) is not None:
1897            character_level = character_level
1898
1899        return activity.PostActivityPlayer(
1900            standing=int(payload["standing"]),
1901            score=int(payload["score"]["basic"]["value"]),
1902            character_id=payload["characterId"],
1903            destiny_user=self.deserialize_destiny_membership(player["destinyUserInfo"]),
1904            character_class=character_class,
1905            character_level=character_level,
1906            race_hash=race_hash,
1907            gender_hash=gender_hash,
1908            class_hash=class_hash,
1909            light_level=int(player["lightLevel"]),
1910            emblem_hash=int(player["emblemHash"]),
1911            values=self._deserialize_activity_values(payload["values"]),
1912            extended_values=self._deserialize_extended_values(payload["extended"]),
1913        )

Deserialize a JSON payload of a post activity player information.

Parameters
Returns
def deserialize_post_activity( self, payload: dict[str, typing.Any]) -> aiobungie.crates.activity.PostActivity:
1925    def deserialize_post_activity(
1926        self, payload: typedefs.JSONObject
1927    ) -> activity.PostActivity:
1928        period = time.clean_date(payload["period"])
1929        details = payload["activityDetails"]
1930        ref_id = int(details["referenceId"])
1931        instance_id = int(details["instanceId"])
1932        mode = enums.GameMode(details["mode"])
1933        modes = [enums.GameMode(int(mode_)) for mode_ in details["modes"]]
1934        is_private = details["isPrivate"]
1935        membership_type = enums.MembershipType(int(details["membershipType"]))
1936        return activity.PostActivity(
1937            net=self._net,
1938            hash=ref_id,
1939            membership_type=membership_type,
1940            instance_id=instance_id,
1941            mode=mode,
1942            modes=modes,
1943            is_private=is_private,
1944            occurred_at=period,
1945            starting_phase=int(payload["startingPhaseIndex"]),
1946            players=[
1947                self.deserialize_post_activity_player(player)
1948                for player in payload["entries"]
1949            ],
1950            teams=[
1951                self._deserialize_post_activity_team(team) for team in payload["teams"]
1952            ],
1953        )

Deserialize a JSON payload of a post activity information.

Parameters
Returns
def deserialize_aggregated_activity( self, payload: dict[str, typing.Any]) -> aiobungie.crates.activity.AggregatedActivity:
1991    def deserialize_aggregated_activity(
1992        self, payload: typedefs.JSONObject
1993    ) -> activity.AggregatedActivity:
1994        return activity.AggregatedActivity(
1995            hash=int(payload["activityHash"]),
1996            values=self._deserialize_aggregated_activity_values(payload["values"]),
1997        )

Deserialize a JSON payload of an aggregated activity.

Parameters
Returns
def deserialize_aggregated_activities( self, payload: dict[str, typing.Any]) -> aiobungie.Iterator[aiobungie.crates.activity.AggregatedActivity]:
1999    def deserialize_aggregated_activities(
2000        self, payload: typedefs.JSONObject
2001    ) -> iterators.Iterator[activity.AggregatedActivity]:
2002        return iterators.Iterator(
2003            [
2004                self.deserialize_aggregated_activity(activity)
2005                for activity in payload["activities"]
2006            ]
2007        )

Deserialize a JSON payload of an array of aggregated activities.

Parameters
Returns
def deserialize_linked_profiles( self, payload: dict[str, typing.Any]) -> aiobungie.crates.profile.LinkedProfile:
2009    def deserialize_linked_profiles(
2010        self, payload: typedefs.JSONObject
2011    ) -> profile.LinkedProfile:
2012        bungie_user = self.deserialize_partial_bungie_user(payload["bnetMembership"])
2013        error_profiles_vec: typing.MutableSequence[user.DestinyMembership] = []
2014        profiles_vec: typing.MutableSequence[user.DestinyMembership] = []
2015
2016        if raw_profile := payload.get("profiles"):
2017            for pfile in raw_profile:
2018                profiles_vec.append(self.deserialize_destiny_membership(pfile))
2019
2020        if raw_profiles_with_errors := payload.get("profilesWithErrors"):
2021            for raw_error_pfile in raw_profiles_with_errors:
2022                if error_pfile := raw_error_pfile.get("infoCard"):
2023                    error_profiles_vec.append(
2024                        self.deserialize_destiny_membership(error_pfile)
2025                    )
2026
2027        return profile.LinkedProfile(
2028            net=self._net,
2029            bungie=bungie_user,
2030            profiles=profiles_vec,
2031            profiles_with_errors=error_profiles_vec,
2032        )

Deserialize a JSON payload of Bungie.net hard linked profile information.

Parameters
Returns
def deserialize_clan_banners( self, payload: dict[str, typing.Any]) -> collections.abc.Sequence[aiobungie.crates.clans.ClanBanner]:
2034    def deserialize_clan_banners(
2035        self, payload: typedefs.JSONObject
2036    ) -> collections.Sequence[clans.ClanBanner]:
2037        banners_seq: typing.MutableSequence[clans.ClanBanner] = []
2038        if banners := payload.get("clanBannerDecals"):
2039            for k, v in banners.items():
2040                banner_obj = clans.ClanBanner(
2041                    id=int(k),
2042                    foreground=assets.Image(v["foregroundPath"]),
2043                    background=assets.Image(v["backgroundPath"]),
2044                )
2045                banners_seq.append(banner_obj)
2046        return banners_seq

Deserialize a JSON array of a clan banners information.

Parameters
Returns
def deserialize_public_milestone_content( self, payload: dict[str, typing.Any]) -> aiobungie.crates.milestones.MilestoneContent:
2048    def deserialize_public_milestone_content(
2049        self, payload: typedefs.JSONObject
2050    ) -> milestones.MilestoneContent:
2051        items_categoris: typedefs.NoneOr[milestones.MilestoneItems] = None
2052        if raw_categories := payload.get("itemCategories"):
2053            for item in raw_categories:
2054                title = undefined.UNDEFINED
2055                if raw_title := item.get("title"):
2056                    if raw_title != typedefs.Unknown:
2057                        title = raw_title
2058                if raw_hashes := item.get("itemHashes"):
2059                    hashes: collections.Sequence[int] = raw_hashes
2060
2061                items_categoris = milestones.MilestoneItems(title=title, hashes=hashes)
2062
2063        about = undefined.UNDEFINED
2064        if (raw_about := payload["about"]) != typedefs.Unknown:
2065            about = raw_about
2066
2067        status = undefined.UNDEFINED
2068        if (raw_status := payload["status"]) != typedefs.Unknown:
2069            status = raw_status
2070
2071        tips: typing.MutableSequence[undefined.UndefinedOr[str]] = []
2072        if raw_tips := payload.get("tips"):
2073            for raw_tip in raw_tips:
2074                if raw_tip == typedefs.Unknown:
2075                    raw_tip = undefined.UNDEFINED
2076                tips.append(raw_tip)
2077
2078        return milestones.MilestoneContent(
2079            about=about, status=status, tips=tips, items=items_categoris
2080        )

Deserialize a JSON payload of milestone content information.

Parameters
Returns
def deserialize_friend( self, payload: dict[str, typing.Any], /) -> aiobungie.crates.friends.Friend:
2082    def deserialize_friend(self, payload: typedefs.JSONObject, /) -> friends.Friend:
2083        name = undefined.UNDEFINED
2084        if (raw_name := payload["bungieGlobalDisplayName"]) != typedefs.Unknown:
2085            name = raw_name
2086
2087        bungie_user: typedefs.NoneOr[user.BungieUser] = None
2088
2089        if raw_bungie_user := payload.get("bungieNetUser"):
2090            bungie_user = self.deserialize_bungie_user(raw_bungie_user)
2091
2092        return friends.Friend(
2093            net=self._net,
2094            id=int(payload["lastSeenAsMembershipId"]),
2095            name=name,
2096            code=payload.get("bungieGlobalDisplayNameCode"),
2097            relationship=enums.Relationship(payload["relationship"]),
2098            user=bungie_user,
2099            online_status=enums.Presence(payload["onlineStatus"]),
2100            online_title=payload["onlineTitle"],
2101            type=enums.MembershipType(payload["lastSeenAsBungieMembershipType"]),
2102        )

Deserialize a JSON payload of a Bungie friend information.

Parameters
Returns
def deserialize_friends( self, payload: dict[str, typing.Any]) -> collections.abc.Sequence[aiobungie.crates.friends.Friend]:
2104    def deserialize_friends(
2105        self, payload: typedefs.JSONObject
2106    ) -> collections.Sequence[friends.Friend]:
2107        mut_seq: typing.MutableSequence[friends.Friend] = []
2108        if raw_friends := payload.get("friends"):
2109            for friend in raw_friends:
2110                mut_seq.append(self.deserialize_friend(friend))
2111        return mut_seq

Deserialize a JSON sequence of Bungie friends information.

This is usually used to deserialize the incoming/outgoing friend requests.

Parameters
Returns
def deserialize_friend_requests( self, payload: dict[str, typing.Any]) -> aiobungie.crates.friends.FriendRequestView:
2113    def deserialize_friend_requests(
2114        self, payload: typedefs.JSONObject
2115    ) -> friends.FriendRequestView:
2116        incoming: typing.MutableSequence[friends.Friend] = []
2117        outgoing: typing.MutableSequence[friends.Friend] = []
2118
2119        if raw_incoming_requests := payload.get("incomingRequests"):
2120            for incoming_request in raw_incoming_requests:
2121                incoming.append(self.deserialize_friend(incoming_request))
2122
2123        if raw_outgoing_requests := payload.get("outgoingRequests"):
2124            for outgoing_request in raw_outgoing_requests:
2125                outgoing.append(self.deserialize_friend(outgoing_request))
2126
2127        return friends.FriendRequestView(incoming=incoming, outgoing=outgoing)

Deserialize a JSON sequence of Bungie friend requests information.

This is used for incoming/outgoing friend requests.

Parameters
Returns
def deserialize_fireteams( self, payload: dict[str, typing.Any]) -> Optional[collections.abc.Sequence[aiobungie.crates.fireteams.Fireteam]]:
2152    def deserialize_fireteams(
2153        self, payload: typedefs.JSONObject
2154    ) -> typedefs.NoneOr[collections.Sequence[fireteams.Fireteam]]:
2155        fireteams_: typing.MutableSequence[fireteams.Fireteam] = []
2156
2157        result: list[typedefs.JSONObject]
2158        if not (result := payload["results"]):
2159            return None
2160        for elem in result:
2161            fireteams_.append(
2162                self._set_fireteam_fields(
2163                    elem, total_results=int(payload["totalResults"])
2164                )
2165            )
2166        return fireteams_

Deserialize a JSON sequence of Bungie fireteams information.

Parameters
Returns
def deserialize_fireteam_destiny_users( self, payload: dict[str, typing.Any]) -> aiobungie.crates.fireteams.FireteamUser:
2168    def deserialize_fireteam_destiny_users(
2169        self, payload: typedefs.JSONObject
2170    ) -> fireteams.FireteamUser:
2171        destiny_obj = self.deserialize_destiny_membership(payload)
2172        # We could helpers.just return a DestinyMembership object but this is
2173        # missing the fireteam display name and id fields.
2174        return fireteams.FireteamUser(
2175            net=self._net,
2176            id=destiny_obj.id,
2177            code=destiny_obj.code,
2178            icon=destiny_obj.icon,
2179            types=destiny_obj.types,
2180            type=destiny_obj.type,
2181            is_public=destiny_obj.is_public,
2182            crossave_override=destiny_obj.crossave_override,
2183            name=destiny_obj.name,
2184            last_seen_name=destiny_obj.last_seen_name,
2185            fireteam_display_name=payload["FireteamDisplayName"],
2186            fireteam_membership_id=enums.MembershipType(
2187                payload["FireteamMembershipType"]
2188            ),
2189        )

Deserialize a JSON payload of Bungie fireteam destiny users information.

Parameters
Returns
def deserialize_fireteam_members( self, payload: dict[str, typing.Any], *, alternatives: bool = False) -> Optional[collections.abc.Sequence[aiobungie.crates.fireteams.FireteamMember]]:
2191    def deserialize_fireteam_members(
2192        self, payload: typedefs.JSONObject, *, alternatives: bool = False
2193    ) -> typing.Optional[collections.Sequence[fireteams.FireteamMember]]:
2194        members_: list[fireteams.FireteamMember] = []
2195        if members := payload.get("Members" if not alternatives else "Alternates"):
2196            for member in members:
2197                bungie_fields = self.deserialize_partial_bungie_user(member)
2198                members_fields = fireteams.FireteamMember(
2199                    destiny_user=self.deserialize_fireteam_destiny_users(member),
2200                    has_microphone=member["hasMicrophone"],
2201                    character_id=int(member["characterId"]),
2202                    date_joined=time.clean_date(member["dateJoined"]),
2203                    last_platform_invite_date=time.clean_date(
2204                        member["lastPlatformInviteAttemptDate"]
2205                    ),
2206                    last_platform_invite_result=int(
2207                        member["lastPlatformInviteAttemptResult"]
2208                    ),
2209                    net=self._net,
2210                    name=bungie_fields.name,
2211                    id=bungie_fields.id,
2212                    icon=bungie_fields.icon,
2213                    is_public=bungie_fields.is_public,
2214                    crossave_override=bungie_fields.crossave_override,
2215                    types=bungie_fields.types,
2216                    type=bungie_fields.type,
2217                )
2218                members_.append(members_fields)
2219        else:
2220            return None
2221        return members_

Deserialize a JSON sequence of Bungie fireteam members information.

Parameters
  • payload (aiobungie.typedefs.JSONObject): The JSON payload.
  • alternatives (bool): If set to True, Then it will deserialize the alternatives data in the payload. If not the it will just deserialize the members data.
Returns
def deserialize_available_fireteams( self, data: dict[str, typing.Any], *, no_results: bool = False) -> Union[aiobungie.crates.fireteams.AvailableFireteam, collections.abc.Sequence[aiobungie.crates.fireteams.AvailableFireteam]]:
2223    def deserialize_available_fireteams(
2224        self,
2225        data: typedefs.JSONObject,
2226        *,
2227        no_results: bool = False,
2228    ) -> typing.Union[
2229        fireteams.AvailableFireteam, collections.Sequence[fireteams.AvailableFireteam]
2230    ]:
2231        fireteams_: list[fireteams.AvailableFireteam] = []
2232
2233        # This needs to be used outside the results
2234        # JSON key.
2235        if no_results is True:
2236            payload = data
2237
2238        if result := payload.get("results"):
2239
2240            for fireteam in result:
2241                found_fireteams = self._set_fireteam_fields(fireteam["Summary"])
2242                fireteams_fields = fireteams.AvailableFireteam(
2243                    id=found_fireteams.id,
2244                    group_id=found_fireteams.group_id,
2245                    platform=found_fireteams.platform,
2246                    activity_type=found_fireteams.activity_type,
2247                    is_immediate=found_fireteams.is_immediate,
2248                    is_public=found_fireteams.is_public,
2249                    is_valid=found_fireteams.is_valid,
2250                    owner_id=found_fireteams.owner_id,
2251                    player_slot_count=found_fireteams.player_slot_count,
2252                    available_player_slots=found_fireteams.available_player_slots,
2253                    available_alternate_slots=found_fireteams.available_alternate_slots,
2254                    title=found_fireteams.title,
2255                    date_created=found_fireteams.date_created,
2256                    locale=found_fireteams.locale,
2257                    last_modified=found_fireteams.last_modified,
2258                    total_results=found_fireteams.total_results,
2259                    members=self.deserialize_fireteam_members(payload),
2260                    alternatives=self.deserialize_fireteam_members(
2261                        payload, alternatives=True
2262                    ),
2263                )
2264            fireteams_.append(fireteams_fields)
2265            if no_results:
2266                return fireteams_fields
2267        return fireteams_

Deserialize a JSON payload of a sequence of/fireteam information.

Parameters
  • payload (aiobungie.typedefs.JSONObject): The JSON payload.
  • no_results (bool): Whether to deserialize the data from results in the payload or not.
Returns
  • typing.Union[aiobungie.crates.fireteams.AvailableFireteam, collections.Sequence[aiobungie.crates.fireteams.AvailableFireteam]] # noqa (E501): An available fireteam or a sequence of available fireteam.
def deserialize_fireteam_party( self, payload: dict[str, typing.Any]) -> aiobungie.crates.fireteams.FireteamParty:
2269    def deserialize_fireteam_party(
2270        self, payload: typedefs.JSONObject
2271    ) -> fireteams.FireteamParty:
2272        last_destination_hash: typing.Optional[int] = None
2273        if raw_dest_hash := payload.get("lastOrbitedDestinationHash"):
2274            last_destination_hash = int(raw_dest_hash)
2275
2276        return fireteams.FireteamParty(
2277            members=[
2278                self._deserialize_fireteam_party_member(member)
2279                for member in payload["partyMembers"]
2280            ],
2281            activity=self._deserialize_fireteam_party_current_activity(
2282                payload["currentActivity"]
2283            ),
2284            settings=self._deserialize_fireteam_party_settings(payload["joinability"]),
2285            last_destination_hash=last_destination_hash,
2286            tracking=payload["tracking"],
2287        )

Deserialize a JSON payload of profileTransitory component response.

Parameters
Returns
def deserialize_seasonal_artifact(self, payload: dict[str, typing.Any]) -> aiobungie.crates.season.Artifact:
2334    def deserialize_seasonal_artifact(
2335        self, payload: typedefs.JSONObject
2336    ) -> season.Artifact:
2337        if raw_artifact := payload.get("seasonalArtifact"):
2338            if points := raw_artifact.get("pointProgression"):
2339                points_prog = progressions.Progression(
2340                    hash=points["progressionHash"],
2341                    level=points["level"],
2342                    cap=points["levelCap"],
2343                    daily_limit=points["dailyLimit"],
2344                    weekly_limit=points["weeklyLimit"],
2345                    current_progress=points["currentProgress"],
2346                    daily_progress=points["dailyProgress"],
2347                    needed=points["progressToNextLevel"],
2348                    next_level=points["nextLevelAt"],
2349                )
2350
2351            if bonus := raw_artifact.get("powerBonusProgression"):
2352                power_bonus_prog = progressions.Progression(
2353                    hash=bonus["progressionHash"],
2354                    level=bonus["level"],
2355                    cap=bonus["levelCap"],
2356                    daily_limit=bonus["dailyLimit"],
2357                    weekly_limit=bonus["weeklyLimit"],
2358                    current_progress=bonus["currentProgress"],
2359                    daily_progress=bonus["dailyProgress"],
2360                    needed=bonus["progressToNextLevel"],
2361                    next_level=bonus["nextLevelAt"],
2362                )
2363            artifact = season.Artifact(
2364                net=self._net,
2365                hash=raw_artifact["artifactHash"],
2366                power_bonus=raw_artifact["powerBonus"],
2367                acquired_points=raw_artifact["pointsAcquired"],
2368                bonus=power_bonus_prog,
2369                points=points_prog,
2370            )
2371        return artifact

Deserialize a JSON payload of a Destiny 2 seasonal artifact information.

Parameters
  • payload (aiobungie.internal.helpers.JsonObject): The JSON payload.
Returns
def deserialize_profile_progression( self, payload: dict[str, typing.Any]) -> aiobungie.crates.profile.ProfileProgression:
2373    def deserialize_profile_progression(
2374        self, payload: typedefs.JSONObject
2375    ) -> profile.ProfileProgression:
2376        return profile.ProfileProgression(
2377            artifact=self.deserialize_seasonal_artifact(payload["data"]),
2378            checklist={
2379                int(check_id): checklists
2380                for check_id, checklists in payload["data"]["checklists"].items()
2381            },
2382        )

Deserialize a JSON payload of a profile progression component.

Parameters
  • payload (aiobungie.internal.helpers.JsonObject): The JSON payload.
Returns
def deserialize_instanced_item( self, payload: dict[str, typing.Any]) -> aiobungie.crates.items.ItemInstance:
2384    def deserialize_instanced_item(
2385        self, payload: typedefs.JSONObject
2386    ) -> items.ItemInstance:
2387        damage_type_hash: typing.Optional[int] = None
2388        if raw_damagetype_hash := payload.get("damageTypeHash"):
2389            damage_type_hash = int(raw_damagetype_hash)
2390
2391        required_hashes: typing.Optional[collections.Collection[int]] = None
2392        if raw_required_hashes := payload.get("unlockHashesRequiredToEquip"):
2393            required_hashes = [int(raw_hash) for raw_hash in raw_required_hashes]
2394
2395        breaker_type: typing.Optional[items.ItemBreakerType] = None
2396        if raw_break_type := payload.get("breakerType"):
2397            breaker_type = items.ItemBreakerType(int(raw_break_type))
2398
2399        breaker_type_hash: typing.Optional[int] = None
2400        if raw_break_type_hash := payload.get("breakerTypeHash"):
2401            breaker_type_hash = int(raw_break_type_hash)
2402
2403        energy: typing.Optional[items.ItemEnergy] = None
2404        if raw_energy := payload.get("energy"):
2405            energy = self.deserialize_item_energy(raw_energy)
2406
2407        primary_stats = None
2408        if raw_primary_stats := payload.get("primaryStat"):
2409            primary_stats = self.deserialize_item_stats_view(raw_primary_stats)
2410
2411        return items.ItemInstance(
2412            damage_type=enums.DamageType(int(payload["damageType"])),
2413            damage_type_hash=damage_type_hash,
2414            primary_stat=primary_stats,
2415            item_level=int(payload["itemLevel"]),
2416            quality=int(payload["quality"]),
2417            is_equipped=payload["isEquipped"],
2418            can_equip=payload["canEquip"],
2419            equip_required_level=int(payload["equipRequiredLevel"]),
2420            required_equip_unlock_hashes=required_hashes,
2421            cant_equip_reason=int(payload["cannotEquipReason"]),
2422            breaker_type=breaker_type,
2423            breaker_type_hash=breaker_type_hash,
2424            energy=energy,
2425        )

Deserialize a JSON object into an instanced item.

Parameters
  • payload (aiobungie.internal.helpers.JsonObject): The JSON payload.
Returns
def deserialize_item_energy( self, payload: dict[str, typing.Any]) -> aiobungie.crates.items.ItemEnergy:
2427    def deserialize_item_energy(self, payload: typedefs.JSONObject) -> items.ItemEnergy:
2428        energy_hash: typing.Optional[int] = None
2429        if raw_energy_hash := payload.get("energyTypeHash"):
2430            energy_hash = int(raw_energy_hash)
2431
2432        return items.ItemEnergy(
2433            hash=energy_hash,
2434            type=items.ItemEnergyType(int(payload["energyType"])),
2435            capacity=int(payload["energyCapacity"]),
2436            used_energy=int(payload["energyUsed"]),
2437            unused_energy=int(payload["energyUnused"]),
2438        )
def deserialize_item_perk(self, payload: dict[str, typing.Any]) -> aiobungie.crates.items.ItemPerk:
2440    def deserialize_item_perk(self, payload: typedefs.JSONObject) -> items.ItemPerk:
2441        perk_hash: typing.Optional[int] = None
2442        if raw_perk_hash := payload.get("perkHash"):
2443            perk_hash = int(raw_perk_hash)
2444
2445        return items.ItemPerk(
2446            hash=perk_hash,
2447            icon=assets.Image(payload["iconPath"]),
2448            is_active=payload["isActive"],
2449            is_visible=payload["visible"],
2450        )
def deserialize_item_socket( self, payload: dict[str, typing.Any]) -> aiobungie.crates.items.ItemSocket:
2452    def deserialize_item_socket(self, payload: typedefs.JSONObject) -> items.ItemSocket:
2453        plug_hash: typing.Optional[int] = None
2454        if raw_plug_hash := payload.get("plugHash"):
2455            plug_hash = int(raw_plug_hash)
2456
2457        enable_fail_indexes: typing.Optional[list[int]] = None
2458        if raw_indexes := payload.get("enableFailIndexes"):
2459            enable_fail_indexes = [int(index) for index in raw_indexes]
2460
2461        return items.ItemSocket(
2462            plug_hash=plug_hash,
2463            is_enabled=payload["isEnabled"],
2464            enable_fail_indexes=enable_fail_indexes,
2465            is_visible=payload.get("visible"),
2466        )
def deserialize_item_stats_view( self, payload: dict[str, typing.Any]) -> aiobungie.crates.items.ItemStatsView:
2468    def deserialize_item_stats_view(
2469        self, payload: typedefs.JSONObject
2470    ) -> items.ItemStatsView:
2471        return items.ItemStatsView(
2472            stat_hash=payload.get("statHash"), value=payload.get("value")
2473        )
def deserialize_plug_item_state( self, payload: dict[str, typing.Any]) -> aiobungie.crates.items.PlugItemState:
2475    def deserialize_plug_item_state(
2476        self, payload: typedefs.JSONObject
2477    ) -> items.PlugItemState:
2478        item_hash: typing.Optional[int] = None
2479        if raw_item_hash := payload.get("plugItemHash"):
2480            item_hash = int(raw_item_hash)
2481
2482        insert_fail_indexes: typedefs.NoneOr[list[int]] = None
2483        if raw_fail_indexes := payload.get("insertFailIndexes"):
2484            insert_fail_indexes = [int(k) for k in raw_fail_indexes]
2485
2486        enable_fail_indexes: typedefs.NoneOr[list[int]] = None
2487        if raw_enabled_indexes := payload.get("enableFailIndexes"):
2488            enable_fail_indexes = [int(k) for k in raw_enabled_indexes]
2489
2490        return items.PlugItemState(
2491            item_hash=item_hash,
2492            insert_fail_indexes=insert_fail_indexes,
2493            enable_fail_indexes=enable_fail_indexes,
2494            is_enabled=payload["enabled"],
2495            can_insert=payload["canInsert"],
2496        )
@typing.final
class FireteamActivity(builtins.int, aiobungie.Enum):
 67@typing.final
 68class FireteamActivity(int, enums.Enum):
 69    """An enum for the fireteam activities."""
 70
 71    ALL = 0
 72    CRUCIBLE = 2
 73    TRIALS_OF_OSIRIS = 3
 74    NIGHTFALL = 4
 75    ANY = 5
 76    GAMBIT = 6
 77    BLIND_WELL = 7
 78    NIGHTMARE_HUNTS = 12
 79    ALTARS_OF_SORROWS = 14
 80    DUNGEON = 15
 81    RAID_LW = 20
 82    RAID_GOS = 21
 83    RAID_DSC = 22
 84    EXO_CHALLENGE = 23
 85    S12_WRATHBORN = 24
 86    EMPIRE_HUNTS = 25
 87    S13_BATTLEGROUNDS = 26
 88    EXOTIC_QUEST = 27
 89    RAID_VOG = 28
 90    S14_EXPUNGE = 30
 91    S15_ASTRAL_ALIGNMENT = 31
 92    S15_SHATTERED_RELAM = 32
 93    SHATTERED_THRONE = 33
 94    PROPHECY = 34
 95    PIT_OF_HERESY = 35
 96    DOE = 36
 97    """Dares of Eternity."""
 98    DUNGEON_GOA = 37
 99    """Grasp of Avarice."""
100    VOW_OF_THE_DISCPILE = 38
101    CAMPAIGN = 39
102    WELLSPRING = 40
103    S16_BATTLEGROUNDS = 41
104    S17_NIGHTMARE_CONTAINMENT = 44
105    S17_SEVER = 45

An enum for the fireteam activities.

CRUCIBLE = <FireteamActivity.CRUCIBLE: 2>
TRIALS_OF_OSIRIS = <FireteamActivity.TRIALS_OF_OSIRIS: 3>
NIGHTFALL = <FireteamActivity.NIGHTFALL: 4>
GAMBIT = <FireteamActivity.GAMBIT: 6>
BLIND_WELL = <FireteamActivity.BLIND_WELL: 7>
NIGHTMARE_HUNTS = <FireteamActivity.NIGHTMARE_HUNTS: 12>
ALTARS_OF_SORROWS = <FireteamActivity.ALTARS_OF_SORROWS: 14>
DUNGEON = <FireteamActivity.DUNGEON: 15>
RAID_LW = <FireteamActivity.RAID_LW: 20>
RAID_GOS = <FireteamActivity.RAID_GOS: 21>
RAID_DSC = <FireteamActivity.RAID_DSC: 22>
EXO_CHALLENGE = <FireteamActivity.EXO_CHALLENGE: 23>
S12_WRATHBORN = <FireteamActivity.S12_WRATHBORN: 24>
EMPIRE_HUNTS = <FireteamActivity.EMPIRE_HUNTS: 25>
S13_BATTLEGROUNDS = <FireteamActivity.S13_BATTLEGROUNDS: 26>
EXOTIC_QUEST = <FireteamActivity.EXOTIC_QUEST: 27>
RAID_VOG = <FireteamActivity.RAID_VOG: 28>
S14_EXPUNGE = <FireteamActivity.S14_EXPUNGE: 30>
S15_ASTRAL_ALIGNMENT = <FireteamActivity.S15_ASTRAL_ALIGNMENT: 31>
S15_SHATTERED_RELAM = <FireteamActivity.S15_SHATTERED_RELAM: 32>
SHATTERED_THRONE = <FireteamActivity.SHATTERED_THRONE: 33>
PROPHECY = <FireteamActivity.PROPHECY: 34>
PIT_OF_HERESY = <FireteamActivity.PIT_OF_HERESY: 35>
DOE = <FireteamActivity.DOE: 36>

Dares of Eternity.

DUNGEON_GOA = <FireteamActivity.DUNGEON_GOA: 37>

Grasp of Avarice.

VOW_OF_THE_DISCPILE = <FireteamActivity.VOW_OF_THE_DISCPILE: 38>
CAMPAIGN = <FireteamActivity.CAMPAIGN: 39>
WELLSPRING = <FireteamActivity.WELLSPRING: 40>
S16_BATTLEGROUNDS = <FireteamActivity.S16_BATTLEGROUNDS: 41>
S17_NIGHTMARE_CONTAINMENT = <FireteamActivity.S17_NIGHTMARE_CONTAINMENT: 44>
S17_SEVER = <FireteamActivity.S17_SEVER: 45>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class FireteamDate(builtins.int, aiobungie.Enum):
131@typing.final
132class FireteamDate(int, enums.Enum):
133    """An enum for fireteam date ranges."""
134
135    ALL = 0
136    NOW = 1
137    TODAY = 2
138    TWO_DAYS = 3
139    THIS_WEEK = 4

An enum for fireteam date ranges.

ALL = <FireteamDate.ALL: 0>
NOW = <FireteamDate.NOW: 1>
TODAY = <FireteamDate.TODAY: 2>
TWO_DAYS = <FireteamDate.TWO_DAYS: 3>
THIS_WEEK = <FireteamDate.THIS_WEEK: 4>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class FireteamLanguage(builtins.str, aiobungie.Enum):
108@typing.final
109class FireteamLanguage(str, enums.Enum):
110    """An enum for fireteams languages filters."""
111
112    ALL = ""
113    ENGLISH = "en"
114    FRENCH = "fr"
115    ESPANOL = "es"
116    DEUTSCH = "de"
117    ITALIAN = "it"
118    JAPANESE = "ja"
119    PORTUGUESE = "pt-br"
120    RUSSIAN = "ru"
121    POLISH = "pl"
122    KOREAN = "ko"
123    # ? China
124    ZH_CHT = "zh-cht"
125    ZH_CHS = "zh-chs"
126
127    def __str__(self) -> str:
128        return str(self.value)

An enum for fireteams languages filters.

ENGLISH = <FireteamLanguage.ENGLISH: en>
FRENCH = <FireteamLanguage.FRENCH: fr>
ESPANOL = <FireteamLanguage.ESPANOL: es>
DEUTSCH = <FireteamLanguage.DEUTSCH: de>
ITALIAN = <FireteamLanguage.ITALIAN: it>
JAPANESE = <FireteamLanguage.JAPANESE: ja>
PORTUGUESE = <FireteamLanguage.PORTUGUESE: pt-br>
RUSSIAN = <FireteamLanguage.RUSSIAN: ru>
POLISH = <FireteamLanguage.POLISH: pl>
KOREAN = <FireteamLanguage.KOREAN: ko>
ZH_CHT = <FireteamLanguage.ZH_CHT: zh-cht>
ZH_CHS = <FireteamLanguage.ZH_CHS: zh-chs>
Inherited Members
Enum
name
value
builtins.str
encode
replace
split
rsplit
join
capitalize
casefold
title
center
count
expandtabs
find
partition
index
ljust
lower
lstrip
rfind
rindex
rjust
rstrip
rpartition
splitlines
strip
swapcase
translate
upper
startswith
endswith
removeprefix
removesuffix
isascii
islower
isupper
istitle
isspace
isdecimal
isdigit
isnumeric
isalpha
isalnum
isidentifier
isprintable
zfill
format
format_map
maketrans
@typing.final
class FireteamPlatform(builtins.int, aiobungie.Enum):
54@typing.final
55class FireteamPlatform(int, enums.Enum):
56    """An enum for fireteam related to bungie fireteams.
57    This is different from the normal `aiobungie.MembershipType`.
58    """
59
60    ANY = 0
61    PSN_NETWORK = 1
62    XBOX_LIVE = 2
63    STEAM = 4
64    STADIA = 5

An enum for fireteam related to bungie fireteams. This is different from the normal aiobungie.MembershipType.

PSN_NETWORK = <FireteamPlatform.PSN_NETWORK: 1>
XBOX_LIVE = <FireteamPlatform.XBOX_LIVE: 2>
STEAM = <FireteamPlatform.STEAM: 4>
STADIA = <FireteamPlatform.STADIA: 5>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
class Flag(enum.Flag):
 97class Flag(__enum.Flag):
 98    """Builtin Python enum flag with extra handlings."""
 99
100    # Needs to type this here for mypy
101    _value_: int
102
103    @property
104    def name(self) -> str:  # type: ignore[override]
105        if self._name_ is None:
106            self._name_ = f"UNKNOWN {self._value_}"
107
108        return self._name_
109
110    @property
111    def value(self) -> int:  # type: ignore[override]
112        return self._value_
113
114    def __str__(self) -> str:
115        return self.name
116
117    def __repr__(self) -> str:
118        return f"<{type(self).__name__}.{self.name}: {self._value_!s}>"
119
120    def __int__(self) -> int:
121        if isinstance(self.value, _ITERABLE):
122            raise TypeError(
123                f"Can't overload {self.value} in {type(self).__name__}, Please use `.value` attribute.",
124            )
125        return int(self.value)
126
127    def __or__(self, other: typing.Union[Flag, int]) -> Flag:
128        return self.__class__(self._value_ | int(other))
129
130    def __xor__(self, other: typing.Union[Flag, int]) -> Flag:
131        return self.__class__(self._value_ ^ int(other))
132
133    def __and__(self, other: typing.Union[Flag, int]) -> Flag:
134        return self.__class__(other & int(other))
135
136    def __invert__(self) -> Flag:
137        return self.__class__(~self._value_)
138
139    def __contains__(self, other: typing.Union[Flag, int]) -> bool:
140        return self.value & int(other) == int(other)

Builtin Python enum flag with extra handlings.

name: str

The name of the Enum member.

value: int

The value of the Enum member.

@attrs.define(auto_exc=True)
class Forbidden(aiobungie.HTTPException):
120@attrs.define(auto_exc=True)
121class Forbidden(HTTPException):
122    """Exception that's raised for when status code 403 occurs."""
123
124    http_status: http.HTTPStatus = attrs.field(
125        default=http.HTTPStatus.FORBIDDEN, init=False
126    )

Exception that's raised for when status code 403 occurs.

Forbidden( *, error_code: int, throttle_seconds: int, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str], message: str, error_status: str, message_data: dict[str, str])
 2def __init__(self, *, error_code, throttle_seconds, url, body, headers, message, error_status, message_data):
 3    self.error_code = error_code
 4    self.throttle_seconds = throttle_seconds
 5    self.url = url
 6    self.body = body
 7    self.headers = headers
 8    self.message = message
 9    self.error_status = error_status
10    self.message_data = message_data
11    self.http_status = attr_dict['http_status'].default
12    BaseException.__init__(self, self.error_code,self.throttle_seconds,self.url,self.body,self.headers,self.message,self.error_status,self.message_data)

Method generated by attrs for class Forbidden.

http_status: http.HTTPStatus

The request response http status.

Inherited Members
HTTPException
error_code
throttle_seconds
url
body
headers
message
error_status
message_data
builtins.BaseException
with_traceback
@typing.final
class GameMode(builtins.int, aiobungie.Enum):
269@typing.final
270class GameMode(int, Enum):
271    """An Enum for all available gamemodes in Destiny 2."""
272
273    NONE = 0
274    STORY = 2
275    STRIKE = 3
276    RAID = 4
277    ALLPVP = 5
278    PATROL = 6
279    ALLPVE = 7
280    RESERVED9 = 9
281    CONTROL = 10
282    RESERVED11 = 11
283    CLASH = 12
284    RESERVED13 = 13
285    CRIMSONDOUBLES = 15
286    NIGHTFALL = 16
287    HEROICNIGHTFALL = 17
288    ALLSTRIKES = 18
289    IRONBANNER = 19
290    RESERVED20 = 20
291    RESERVED21 = 21
292    RESERVED22 = 22
293    RESERVED24 = 24
294    ALLMAYHEM = 25
295    RESERVED26 = 26
296    RESERVED27 = 27
297    RESERVED28 = 28
298    RESERVED29 = 29
299    RESERVED30 = 30
300    SUPREMACY = 31
301    PRIVATEMATCHESALL = 32
302    SURVIVAL = 37
303    COUNTDOWN = 38
304    TRIALSOFTHENINE = 39
305    SOCIAL = 40
306    TRIALSCOUNTDOWN = 41
307    TRIALSSURVIVAL = 42
308    IRONBANNERCONTROL = 43
309    IRONBANNERCLASH = 44
310    IRONBANNERSUPREMACY = 45
311    SCOREDNIGHTFALL = 46
312    SCOREDHEROICNIGHTFALL = 47
313    RUMBLE = 48
314    ALLDOUBLES = 49
315    DOUBLES = 50
316    PRIVATEMATCHESCLASH = 51
317    PRIVATEMATCHESCONTROL = 52
318    PRIVATEMATCHESSUPREMACY = 53
319    PRIVATEMATCHESCOUNTDOWN = 54
320    PRIVATEMATCHESSURVIVAL = 55
321    PRIVATEMATCHESMAYHEM = 56
322    PRIVATEMATCHESRUMBLE = 57
323    HEROICADVENTURE = 58
324    SHOWDOWN = 59
325    LOCKDOWN = 60
326    SCORCHED = 61
327    SCORCHEDTEAM = 62
328    GAMBIT = 63
329    ALLPVECOMPETITIVE = 64
330    BREAKTHROUGH = 65
331    BLACKARMORYRUN = 66
332    SALVAGE = 67
333    IRONBANNERSALVAGE = 68
334    PVPCOMPETITIVE = 69
335    PVPQUICKPLAY = 70
336    CLASHQUICKPLAY = 71
337    CLASHCOMPETITIVE = 72
338    CONTROLQUICKPLAY = 73
339    CONTROLCOMPETITIVE = 74
340    GAMBITPRIME = 75
341    RECKONING = 76
342    MENAGERIE = 77
343    VEXOFFENSIVE = 78
344    NIGHTMAREHUNT = 79
345    ELIMINATION = 80
346    MOMENTUM = 81
347    DUNGEON = 82
348    SUNDIAL = 83
349    TRIALS_OF_OSIRIS = 84
350    DARES = 85
351    OFFENSIVE = 86
352    LOSTSECTOR = 87
353    RIFT = 88
354    ZONECONTROL = 89
355    IRONBANNERRIFT = 90

An Enum for all available gamemodes in Destiny 2.

NONE = <GameMode.NONE: 0>
STORY = <GameMode.STORY: 2>
STRIKE = <GameMode.STRIKE: 3>
RAID = <GameMode.RAID: 4>
ALLPVP = <GameMode.ALLPVP: 5>
PATROL = <GameMode.PATROL: 6>
ALLPVE = <GameMode.ALLPVE: 7>
RESERVED9 = <GameMode.RESERVED9: 9>
CONTROL = <GameMode.CONTROL: 10>
RESERVED11 = <GameMode.RESERVED11: 11>
CLASH = <GameMode.CLASH: 12>
RESERVED13 = <GameMode.RESERVED13: 13>
CRIMSONDOUBLES = <GameMode.CRIMSONDOUBLES: 15>
NIGHTFALL = <GameMode.NIGHTFALL: 16>
HEROICNIGHTFALL = <GameMode.HEROICNIGHTFALL: 17>
ALLSTRIKES = <GameMode.ALLSTRIKES: 18>
IRONBANNER = <GameMode.IRONBANNER: 19>
RESERVED20 = <GameMode.RESERVED20: 20>
RESERVED21 = <GameMode.RESERVED21: 21>
RESERVED22 = <GameMode.RESERVED22: 22>
RESERVED24 = <GameMode.RESERVED24: 24>
ALLMAYHEM = <GameMode.ALLMAYHEM: 25>
RESERVED26 = <GameMode.RESERVED26: 26>
RESERVED27 = <GameMode.RESERVED27: 27>
RESERVED28 = <GameMode.RESERVED28: 28>
RESERVED29 = <GameMode.RESERVED29: 29>
RESERVED30 = <GameMode.RESERVED30: 30>
SUPREMACY = <GameMode.SUPREMACY: 31>
PRIVATEMATCHESALL = <GameMode.PRIVATEMATCHESALL: 32>
SURVIVAL = <GameMode.SURVIVAL: 37>
COUNTDOWN = <GameMode.COUNTDOWN: 38>
TRIALSOFTHENINE = <GameMode.TRIALSOFTHENINE: 39>
SOCIAL = <GameMode.SOCIAL: 40>
TRIALSCOUNTDOWN = <GameMode.TRIALSCOUNTDOWN: 41>
TRIALSSURVIVAL = <GameMode.TRIALSSURVIVAL: 42>
IRONBANNERCONTROL = <GameMode.IRONBANNERCONTROL: 43>
IRONBANNERCLASH = <GameMode.IRONBANNERCLASH: 44>
IRONBANNERSUPREMACY = <GameMode.IRONBANNERSUPREMACY: 45>
SCOREDNIGHTFALL = <GameMode.SCOREDNIGHTFALL: 46>
SCOREDHEROICNIGHTFALL = <GameMode.SCOREDHEROICNIGHTFALL: 47>
RUMBLE = <GameMode.RUMBLE: 48>
ALLDOUBLES = <GameMode.ALLDOUBLES: 49>
DOUBLES = <GameMode.DOUBLES: 50>
PRIVATEMATCHESCLASH = <GameMode.PRIVATEMATCHESCLASH: 51>
PRIVATEMATCHESCONTROL = <GameMode.PRIVATEMATCHESCONTROL: 52>
PRIVATEMATCHESSUPREMACY = <GameMode.PRIVATEMATCHESSUPREMACY: 53>
PRIVATEMATCHESCOUNTDOWN = <GameMode.PRIVATEMATCHESCOUNTDOWN: 54>
PRIVATEMATCHESSURVIVAL = <GameMode.PRIVATEMATCHESSURVIVAL: 55>
PRIVATEMATCHESMAYHEM = <GameMode.PRIVATEMATCHESMAYHEM: 56>
PRIVATEMATCHESRUMBLE = <GameMode.PRIVATEMATCHESRUMBLE: 57>
HEROICADVENTURE = <GameMode.HEROICADVENTURE: 58>
SHOWDOWN = <GameMode.SHOWDOWN: 59>
LOCKDOWN = <GameMode.LOCKDOWN: 60>
SCORCHED = <GameMode.SCORCHED: 61>
SCORCHEDTEAM = <GameMode.SCORCHEDTEAM: 62>
GAMBIT = <GameMode.GAMBIT: 63>
ALLPVECOMPETITIVE = <GameMode.ALLPVECOMPETITIVE: 64>
BREAKTHROUGH = <GameMode.BREAKTHROUGH: 65>
BLACKARMORYRUN = <GameMode.BLACKARMORYRUN: 66>
SALVAGE = <GameMode.SALVAGE: 67>
IRONBANNERSALVAGE = <GameMode.IRONBANNERSALVAGE: 68>
PVPCOMPETITIVE = <GameMode.PVPCOMPETITIVE: 69>
PVPQUICKPLAY = <GameMode.PVPQUICKPLAY: 70>
CLASHQUICKPLAY = <GameMode.CLASHQUICKPLAY: 71>
CLASHCOMPETITIVE = <GameMode.CLASHCOMPETITIVE: 72>
CONTROLQUICKPLAY = <GameMode.CONTROLQUICKPLAY: 73>
CONTROLCOMPETITIVE = <GameMode.CONTROLCOMPETITIVE: 74>
GAMBITPRIME = <GameMode.GAMBITPRIME: 75>
RECKONING = <GameMode.RECKONING: 76>
MENAGERIE = <GameMode.MENAGERIE: 77>
VEXOFFENSIVE = <GameMode.VEXOFFENSIVE: 78>
NIGHTMAREHUNT = <GameMode.NIGHTMAREHUNT: 79>
ELIMINATION = <GameMode.ELIMINATION: 80>
MOMENTUM = <GameMode.MOMENTUM: 81>
DUNGEON = <GameMode.DUNGEON: 82>
SUNDIAL = <GameMode.SUNDIAL: 83>
TRIALS_OF_OSIRIS = <GameMode.TRIALS_OF_OSIRIS: 84>
DARES = <GameMode.DARES: 85>
OFFENSIVE = <GameMode.OFFENSIVE: 86>
LOSTSECTOR = <GameMode.LOSTSECTOR: 87>
RIFT = <GameMode.RIFT: 88>
ZONECONTROL = <GameMode.ZONECONTROL: 89>
IRONBANNERRIFT = <GameMode.IRONBANNERRIFT: 90>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class GatingScope(builtins.int, aiobungie.Enum):
58@typing.final
59class GatingScope(int, enums.Enum):
60    """An enum represents restrictive type of gating that is being performed by an entity.
61
62    This is useful as a shortcut to avoid a lot of lookups when determining whether the gating on an Entity
63    applies to everyone equally, or to their specific Profile or Character states.
64    """
65
66    NONE = 0
67    GLOBAL = 1
68    CLAN = 2
69    PROFILE = 3
70    CHARACTER = 4
71    ITEM = 5
72    ASSUMED_WORST_CASE = 6

An enum represents restrictive type of gating that is being performed by an entity.

This is useful as a shortcut to avoid a lot of lookups when determining whether the gating on an Entity applies to everyone equally, or to their specific Profile or Character states.

NONE = <GatingScope.NONE: 0>
GLOBAL = <GatingScope.GLOBAL: 1>
CLAN = <GatingScope.CLAN: 2>
PROFILE = <GatingScope.PROFILE: 3>
CHARACTER = <GatingScope.CHARACTER: 4>
ITEM = <GatingScope.ITEM: 5>
ASSUMED_WORST_CASE = <GatingScope.ASSUMED_WORST_CASE: 6>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Gender(builtins.int, aiobungie.Enum):
484@typing.final
485class Gender(int, Enum):
486    """An Enum for Destiny Genders."""
487
488    MALE = 0
489    FEMALE = 1
490    UNKNOWN = 2

An Enum for Destiny Genders.

MALE = <Gender.MALE: 0>
FEMALE = <Gender.FEMALE: 1>
UNKNOWN = <Gender.UNKNOWN: 2>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class GroupType(builtins.int, aiobungie.Enum):
653@typing.final
654class GroupType(int, Enum):
655    """An enums for the known bungie group types."""
656
657    GENERAL = 0
658    CLAN = 1

An enums for the known bungie group types.

GENERAL = <GroupType.GENERAL: 0>
CLAN = <GroupType.CLAN: 1>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@attrs.define(auto_exc=True)
class HTTPError(aiobungie.AiobungieError):
62@attrs.define(auto_exc=True)
63class HTTPError(AiobungieError):
64    """Exception base used for HTTP request errors."""
65
66    message: str
67    """The error message."""
68
69    http_status: http.HTTPStatus
70    """The response status."""

Exception base used for HTTP request errors.

HTTPError(message: str, http_status: http.HTTPStatus)
2def __init__(self, message, http_status):
3    self.message = message
4    self.http_status = http_status
5    BaseException.__init__(self, self.message,self.http_status)

Method generated by attrs for class HTTPError.

message: str

The error message.

http_status: http.HTTPStatus

The response status.

Inherited Members
builtins.BaseException
with_traceback
@attrs.define(auto_exc=True, kw_only=True)
class HTTPException(aiobungie.HTTPError):
 73@attrs.define(auto_exc=True, kw_only=True)
 74class HTTPException(HTTPError):
 75    """Exception base internally used for an HTTP request response errors."""
 76
 77    error_code: int
 78    """The returned Bungie error status code."""
 79
 80    http_status: http.HTTPStatus
 81    """The request response http status."""
 82
 83    throttle_seconds: int
 84    """The Bungie response throttle seconds."""
 85
 86    url: typing.Optional[typedefs.StrOrURL]
 87    """The URL/endpoint caused this error."""
 88
 89    body: typing.Any
 90    """The response body."""
 91
 92    headers: multidict.CIMultiDictProxy[str]
 93    """The response headers."""
 94
 95    message: str
 96    """A Bungie human readable message describes the cause of the error."""
 97
 98    error_status: str
 99    """A Bungie short error status describes the cause of the error."""
100
101    message_data: dict[str, str]
102    """A dict of string key, value that includes each cause of the error
103    to a message describes information about that error.
104    """
105
106    def __str__(self) -> str:
107        if self.message:
108            message_body = self.message
109
110        if self.error_status:
111            error_status_body = self.error_status
112
113        return (
114            f"{self.http_status.name.replace('_', '').title()} {self.http_status.value}: "
115            f"Error status: {error_status_body}, Error message: {message_body} from {self.url} "
116            f"{str(self.body)}"
117        )

Exception base internally used for an HTTP request response errors.

HTTPException( *, error_code: int, http_status: http.HTTPStatus, throttle_seconds: int, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str], message: str, error_status: str, message_data: dict[str, str])
 2def __init__(self, *, error_code, http_status, throttle_seconds, url, body, headers, message, error_status, message_data):
 3    self.error_code = error_code
 4    self.http_status = http_status
 5    self.throttle_seconds = throttle_seconds
 6    self.url = url
 7    self.body = body
 8    self.headers = headers
 9    self.message = message
10    self.error_status = error_status
11    self.message_data = message_data
12    BaseException.__init__(self, self.error_code,self.http_status,self.throttle_seconds,self.url,self.body,self.headers,self.message,self.error_status,self.message_data)

Method generated by attrs for class HTTPException.

error_code: int

The returned Bungie error status code.

http_status: http.HTTPStatus

The request response http status.

throttle_seconds: int

The Bungie response throttle seconds.

url: Union[str, yarl.URL, NoneType]

The URL/endpoint caused this error.

body: Any

The response body.

headers: multidict._multidict.CIMultiDictProxy[str]

The response headers.

message: str

A Bungie human readable message describes the cause of the error.

error_status: str

A Bungie short error status describes the cause of the error.

message_data: dict[str, str]

A dict of string key, value that includes each cause of the error to a message describes information about that error.

Inherited Members
builtins.BaseException
with_traceback
class Image:
 72class Image:
 73    """Representation of an image/avatar/picture at Bungie.
 74
 75    Example
 76    -------
 77    ```py
 78    from aiobungie import Image
 79    img = Image("img/destiny_content/pgcr/raid_eclipse.jpg")
 80    print(img)
 81    # https://www.bungie.net/img/destiny_content/pgcr/raid_eclipse.jpg
 82
 83    # Stream the image.
 84    async for chunk in img:
 85        # Byte chunks of the image.
 86        print(chunk)
 87
 88    # Save the image to a file.
 89    await img.save("file_name", "/my/path/to/save/to", "jpeg")
 90    ```
 91
 92    Parameters
 93    ----------
 94    path : `str | None`
 95        The path to the image. If `None`, the default missing image path will be used.
 96    """
 97
 98    __slots__ = ("_path",)
 99
100    def __init__(self, path: typing.Optional[str] = None) -> None:
101        self._path = path
102
103    @property
104    def is_missing(self) -> bool:
105        return not self._path
106
107    @property
108    def url(self) -> str:
109        """The URL to the image."""
110        return self.create_url()
111
112    @staticmethod
113    def missing_path() -> str:
114        """Returns the path to the missing Bungie image."""
115        return "img/misc/missing_icon_d2.png"
116
117    def create_url(self) -> str:
118        """Creates a full URL to the image path.
119
120        Returns
121        -------
122        str
123            The URL to the image.
124        """
125        return f"{url.BASE}/{self._path if self._path else self.missing_path()}"
126
127    async def save(
128        self,
129        file_name: str,
130        path: typing.Union[pathlib.Path, str],
131        /,
132        mime_type: typing.Optional[typing.Union[MimeType, str]] = None,
133    ) -> None:
134        """Saves the image to a file.
135
136        Parameters
137        ----------
138        file_name : `str`
139            A name for the file to save the image to.
140        path : `pathlib.Path | str`
141            A path tp save the image to.
142
143        Other Parameters
144        ----------------
145        mime_type : `MimeType | str`
146            Optional MIME type of the image.
147
148        Raises
149        ------
150        `FileNotFoundError`
151            If the path provided does not exist.
152        `RuntimeError`
153            If the image could not be saved.
154        `PermissionError`
155            If the path provided is not writable or does not have write permissions.
156        """
157        if isinstance(path, pathlib.Path) and not path.exists():
158            raise FileNotFoundError(f"File does not exist: {path!r}")
159
160        if self.is_missing:
161            return
162
163        mimetype = mime_type or MimeType.PNG
164        path = pathlib.Path(path)
165
166        loop = helpers.get_or_make_loop()
167        pool = concurrent.futures.ThreadPoolExecutor()
168
169        try:
170            with pool:
171                await loop.run_in_executor(
172                    pool, _write, path, file_name, mimetype, await self.read()
173                )
174                _LOGGER.info("Saved image to %s", file_name)
175
176        except asyncio.CancelledError:
177            pass
178
179        except Exception as err:
180            raise RuntimeError("Encountered an error while saving image.") from err
181
182    async def read(self) -> bytes:
183        """Read this image bytes.
184
185        Returns
186        -------
187        `bytes`
188            The bytes of this image.
189        """
190        client_session = aiohttp.ClientSession()
191
192        try:
193            await client_session.__aenter__()
194            response = await client_session.get(self.create_url())
195
196            if 300 >= response.status >= 200:
197                reader = await response.read()
198
199        except Exception as exc:
200            raise RuntimeError(f"Failed to read image: {exc}") from None
201        finally:
202            await client_session.__aexit__(None, None, None)
203        return reader
204
205    async def iter(self) -> collections.AsyncGenerator[bytes, None]:
206        """Iterates over the image bytes lazily.
207
208        Example
209        -------
210        import aiobungie
211
212        resource = aiobungie.Image("img/misc/missing_icon_d2.png")
213        async for chunk in resource.iter():
214            print(chunk)
215
216        Returns
217        -------
218        `collections.AsyncGenerator[bytes, None]`
219            An async generator of the image bytes.
220        """
221
222        async for chunk in self:
223            yield chunk
224
225    def __repr__(self) -> str:
226        return f"Image(url={self.create_url()})"
227
228    def __str__(self) -> str:
229        return self.create_url()
230
231    def __aiter__(self) -> Image:
232        return self
233
234    async def __anext__(self) -> bytes:
235        return await self.read()
236
237    def __await__(self) -> collections.Generator[None, None, bytes]:
238        return self.__anext__().__await__()

Representation of an image/avatar/picture at Bungie.

Example
from aiobungie import Image
img = Image("img/destiny_content/pgcr/raid_eclipse.jpg")
print(img)
# https://www.bungie.net/img/destiny_content/pgcr/raid_eclipse.jpg

# Stream the image.
async for chunk in img:
    # Byte chunks of the image.
    print(chunk)

# Save the image to a file.
await img.save("file_name", "/my/path/to/save/to", "jpeg")
Parameters
  • path (str | None): The path to the image. If None, the default missing image path will be used.
Image(path: Optional[str] = None)
100    def __init__(self, path: typing.Optional[str] = None) -> None:
101        self._path = path
url: str

The URL to the image.

@staticmethod
def missing_path() -> str:
112    @staticmethod
113    def missing_path() -> str:
114        """Returns the path to the missing Bungie image."""
115        return "img/misc/missing_icon_d2.png"

Returns the path to the missing Bungie image.

def create_url(self) -> str:
117    def create_url(self) -> str:
118        """Creates a full URL to the image path.
119
120        Returns
121        -------
122        str
123            The URL to the image.
124        """
125        return f"{url.BASE}/{self._path if self._path else self.missing_path()}"

Creates a full URL to the image path.

Returns
  • str: The URL to the image.
async def save( self, file_name: str, path: Union[pathlib.Path, str], /, mime_type: Union[aiobungie.internal.assets.MimeType, str, NoneType] = None) -> None:
127    async def save(
128        self,
129        file_name: str,
130        path: typing.Union[pathlib.Path, str],
131        /,
132        mime_type: typing.Optional[typing.Union[MimeType, str]] = None,
133    ) -> None:
134        """Saves the image to a file.
135
136        Parameters
137        ----------
138        file_name : `str`
139            A name for the file to save the image to.
140        path : `pathlib.Path | str`
141            A path tp save the image to.
142
143        Other Parameters
144        ----------------
145        mime_type : `MimeType | str`
146            Optional MIME type of the image.
147
148        Raises
149        ------
150        `FileNotFoundError`
151            If the path provided does not exist.
152        `RuntimeError`
153            If the image could not be saved.
154        `PermissionError`
155            If the path provided is not writable or does not have write permissions.
156        """
157        if isinstance(path, pathlib.Path) and not path.exists():
158            raise FileNotFoundError(f"File does not exist: {path!r}")
159
160        if self.is_missing:
161            return
162
163        mimetype = mime_type or MimeType.PNG
164        path = pathlib.Path(path)
165
166        loop = helpers.get_or_make_loop()
167        pool = concurrent.futures.ThreadPoolExecutor()
168
169        try:
170            with pool:
171                await loop.run_in_executor(
172                    pool, _write, path, file_name, mimetype, await self.read()
173                )
174                _LOGGER.info("Saved image to %s", file_name)
175
176        except asyncio.CancelledError:
177            pass
178
179        except Exception as err:
180            raise RuntimeError("Encountered an error while saving image.") from err

Saves the image to a file.

Parameters
  • file_name (str): A name for the file to save the image to.
  • path (pathlib.Path | str): A path tp save the image to.
Other Parameters
  • mime_type (MimeType | str): Optional MIME type of the image.
Raises
  • FileNotFoundError: If the path provided does not exist.
  • RuntimeError: If the image could not be saved.
  • PermissionError: If the path provided is not writable or does not have write permissions.
async def read(self) -> bytes:
182    async def read(self) -> bytes:
183        """Read this image bytes.
184
185        Returns
186        -------
187        `bytes`
188            The bytes of this image.
189        """
190        client_session = aiohttp.ClientSession()
191
192        try:
193            await client_session.__aenter__()
194            response = await client_session.get(self.create_url())
195
196            if 300 >= response.status >= 200:
197                reader = await response.read()
198
199        except Exception as exc:
200            raise RuntimeError(f"Failed to read image: {exc}") from None
201        finally:
202            await client_session.__aexit__(None, None, None)
203        return reader

Read this image bytes.

Returns
  • bytes: The bytes of this image.
async def iter(self) -> collections.abc.AsyncGenerator[bytes, None]:
205    async def iter(self) -> collections.AsyncGenerator[bytes, None]:
206        """Iterates over the image bytes lazily.
207
208        Example
209        -------
210        import aiobungie
211
212        resource = aiobungie.Image("img/misc/missing_icon_d2.png")
213        async for chunk in resource.iter():
214            print(chunk)
215
216        Returns
217        -------
218        `collections.AsyncGenerator[bytes, None]`
219            An async generator of the image bytes.
220        """
221
222        async for chunk in self:
223            yield chunk

Iterates over the image bytes lazily.

Example

import aiobungie

resource = aiobungie.Image("img/misc/missing_icon_d2.png") async for chunk in resource.iter(): print(chunk)

Returns
  • collections.AsyncGenerator[bytes, None]: An async generator of the image bytes.
@attrs.define(auto_exc=True)
class InternalServerError(aiobungie.HTTPException):
190@attrs.define(auto_exc=True)
191class InternalServerError(HTTPException):
192    """Raised for 5xx internal server errors."""

Raised for 5xx internal server errors.

InternalServerError( *, error_code: int, http_status: http.HTTPStatus, throttle_seconds: int, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str], message: str, error_status: str, message_data: dict[str, str])
 2def __init__(self, *, error_code, http_status, throttle_seconds, url, body, headers, message, error_status, message_data):
 3    self.error_code = error_code
 4    self.http_status = http_status
 5    self.throttle_seconds = throttle_seconds
 6    self.url = url
 7    self.body = body
 8    self.headers = headers
 9    self.message = message
10    self.error_status = error_status
11    self.message_data = message_data
12    BaseException.__init__(self, self.error_code,self.http_status,self.throttle_seconds,self.url,self.body,self.headers,self.message,self.error_status,self.message_data)

Method generated by attrs for class InternalServerError.

Inherited Members
HTTPException
error_code
http_status
throttle_seconds
url
body
headers
message
error_status
message_data
builtins.BaseException
with_traceback
@typing.final
class ItemBindStatus(builtins.int, aiobungie.Enum):
719@typing.final
720class ItemBindStatus(int, Enum):
721    """An enum for Destiny 2 items bind status."""
722
723    NOT_BOUND = 0
724    BOUND_TO_CHARACTER = 1
725    BOUND_TO_ACCOUNT = 2
726    BOUNT_TO_GUILD = 3

An enum for Destiny 2 items bind status.

NOT_BOUND = <ItemBindStatus.NOT_BOUND: 0>
BOUND_TO_CHARACTER = <ItemBindStatus.BOUND_TO_CHARACTER: 1>
BOUND_TO_ACCOUNT = <ItemBindStatus.BOUND_TO_ACCOUNT: 2>
BOUNT_TO_GUILD = <ItemBindStatus.BOUNT_TO_GUILD: 3>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class ItemLocation(builtins.int, aiobungie.Enum):
729@typing.final
730class ItemLocation(int, Enum):
731    """An enum for Destiny 2 items location."""
732
733    UNKNOWN = 0
734    INVENTORY = 1
735    VAULT = 2
736    VENDOR = 3
737    POSTMASTER = 4

An enum for Destiny 2 items location.

UNKNOWN = <ItemLocation.UNKNOWN: 0>
INVENTORY = <ItemLocation.INVENTORY: 1>
VAULT = <ItemLocation.VAULT: 2>
VENDOR = <ItemLocation.VENDOR: 3>
POSTMASTER = <ItemLocation.POSTMASTER: 4>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class ItemState(aiobungie.Flag):
754@typing.final
755class ItemState(Flag):
756    """An enum for Destiny 2 item states."""
757
758    NONE = 0
759    LOCKED = 1 << 0
760    TRACKED = 1 << 1
761    MASTERWORKED = 1 << 2
762    CRAFTED = 1 << 3
763    """If this bit is set, the item has been 'crafted' by the player."""
764    HIGHLITED_OBJECTIVE = 1 << 4
765    """If this bit is set, the item is a 'highlighted' objective."""

An enum for Destiny 2 item states.

NONE = <ItemState.NONE: 0>
LOCKED = <ItemState.LOCKED: 1>
TRACKED = <ItemState.TRACKED: 2>
MASTERWORKED = <ItemState.MASTERWORKED: 4>
CRAFTED = <ItemState.CRAFTED: 8>

If this bit is set, the item has been 'crafted' by the player.

HIGHLITED_OBJECTIVE = <ItemState.HIGHLITED_OBJECTIVE: 16>

If this bit is set, the item is a 'highlighted' objective.

Inherited Members
Flag
name
value
@typing.final
class ItemSubType(builtins.int, aiobungie.Enum):
586@typing.final
587class ItemSubType(int, Enum):
588    """An enum for Destiny 2 inventory items subtype."""
589
590    NONE = 0
591    AUTORIFLE = 6
592    SHOTGUN = 7
593    MACHINEGUN = 8
594    HANDCANNON = 9
595    ROCKETLAUNCHER = 10
596    FUSIONRIFLE = 11
597    SNIPERRIFLE = 12
598    PULSERIFLE = 13
599    SCOUTRIFLE = 14
600    SIDEARM = 17
601    SWORD = 18
602    MASK = 19
603    SHADER = 20
604    ORNAMENT = 21
605    FUSIONRIFLELINE = 22
606    GRENADELAUNCHER = 23
607    SUBMACHINEGUN = 24
608    TRACERIFLE = 25
609    HELMETARMOR = 26
610    GAUNTLETSARMOR = 27
611    CHESTARMOR = 28
612    LEGARMOR = 29
613    CLASSARMOR = 30
614    BOW = 31
615    DUMMYREPEATABLEBOUNTY = 32

An enum for Destiny 2 inventory items subtype.

NONE = <ItemSubType.NONE: 0>
AUTORIFLE = <ItemSubType.AUTORIFLE: 6>
SHOTGUN = <ItemSubType.SHOTGUN: 7>
MACHINEGUN = <ItemSubType.MACHINEGUN: 8>
HANDCANNON = <ItemSubType.HANDCANNON: 9>
ROCKETLAUNCHER = <ItemSubType.ROCKETLAUNCHER: 10>
FUSIONRIFLE = <ItemSubType.FUSIONRIFLE: 11>
SNIPERRIFLE = <ItemSubType.SNIPERRIFLE: 12>
PULSERIFLE = <ItemSubType.PULSERIFLE: 13>
SCOUTRIFLE = <ItemSubType.SCOUTRIFLE: 14>
SIDEARM = <ItemSubType.SIDEARM: 17>
SWORD = <ItemSubType.SWORD: 18>
MASK = <ItemSubType.MASK: 19>
SHADER = <ItemSubType.SHADER: 20>
ORNAMENT = <ItemSubType.ORNAMENT: 21>
FUSIONRIFLELINE = <ItemSubType.FUSIONRIFLELINE: 22>
GRENADELAUNCHER = <ItemSubType.GRENADELAUNCHER: 23>
SUBMACHINEGUN = <ItemSubType.SUBMACHINEGUN: 24>
TRACERIFLE = <ItemSubType.TRACERIFLE: 25>
HELMETARMOR = <ItemSubType.HELMETARMOR: 26>
GAUNTLETSARMOR = <ItemSubType.GAUNTLETSARMOR: 27>
CHESTARMOR = <ItemSubType.CHESTARMOR: 28>
LEGARMOR = <ItemSubType.LEGARMOR: 29>
CLASSARMOR = <ItemSubType.CLASSARMOR: 30>
BOW = <ItemSubType.BOW: 31>
DUMMYREPEATABLEBOUNTY = <ItemSubType.DUMMYREPEATABLEBOUNTY: 32>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class ItemTier(builtins.int, aiobungie.Enum):
618@typing.final
619class ItemTier(int, Enum):
620    """An enum for a Destiny 2 item tier."""
621
622    NONE = 0
623    BASIC = 3340296461
624    COMMON = 2395677314
625    RARE = 2127292149
626    LEGENDERY = 4008398120
627    EXOTIC = 2759499571

An enum for a Destiny 2 item tier.

NONE = <ItemTier.NONE: 0>
BASIC = <ItemTier.BASIC: 3340296461>
COMMON = <ItemTier.COMMON: 2395677314>
RARE = <ItemTier.RARE: 2127292149>
LEGENDERY = <ItemTier.LEGENDERY: 4008398120>
EXOTIC = <ItemTier.EXOTIC: 2759499571>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class ItemType(builtins.int, aiobungie.Enum):
553@typing.final
554class ItemType(int, Enum):
555    """Enums for Destiny2's item types."""
556
557    NONE = 0
558    CURRENCY = 1
559    ARMOR = 2
560    WEAPON = 3
561    MESSAGE = 7
562    ENGRAM = 8
563    CONSUMABLE = 9
564    EXCHANGEMATERIAL = 10
565    MISSIONREWARD = 11
566    QUESTSTEP = 12
567    QUESTSTEPCOMPLETE = 13
568    EMBLEM = 14
569    QUEST = 15
570    SUBCLASS = 16
571    CLANBANNER = 17
572    AURA = 18
573    MOD = 19
574    DUMMY = 20
575    SHIP = 21
576    VEHICLE = 22
577    EMOTE = 23
578    GHOST = 24
579    PACKAGE = 25
580    BOUNTY = 26
581    WRAPPER = 27
582    SEASONALARTIFACT = 28
583    FINISHER = 29

Enums for Destiny2's item types.

NONE = <ItemType.NONE: 0>
CURRENCY = <ItemType.CURRENCY: 1>
ARMOR = <ItemType.ARMOR: 2>
WEAPON = <ItemType.WEAPON: 3>
MESSAGE = <ItemType.MESSAGE: 7>
ENGRAM = <ItemType.ENGRAM: 8>
CONSUMABLE = <ItemType.CONSUMABLE: 9>
EXCHANGEMATERIAL = <ItemType.EXCHANGEMATERIAL: 10>
MISSIONREWARD = <ItemType.MISSIONREWARD: 11>
QUESTSTEP = <ItemType.QUESTSTEP: 12>
QUESTSTEPCOMPLETE = <ItemType.QUESTSTEPCOMPLETE: 13>
EMBLEM = <ItemType.EMBLEM: 14>
QUEST = <ItemType.QUEST: 15>
SUBCLASS = <ItemType.SUBCLASS: 16>
CLANBANNER = <ItemType.CLANBANNER: 17>
AURA = <ItemType.AURA: 18>
MOD = <ItemType.MOD: 19>
DUMMY = <ItemType.DUMMY: 20>
SHIP = <ItemType.SHIP: 21>
VEHICLE = <ItemType.VEHICLE: 22>
EMOTE = <ItemType.EMOTE: 23>
GHOST = <ItemType.GHOST: 24>
PACKAGE = <ItemType.PACKAGE: 25>
BOUNTY = <ItemType.BOUNTY: 26>
WRAPPER = <ItemType.WRAPPER: 27>
SEASONALARTIFACT = <ItemType.SEASONALARTIFACT: 28>
FINISHER = <ItemType.FINISHER: 29>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
class Iterator(typing.Generic[~Item]):
 45class Iterator(typing.Generic[Item]):
 46    """A Flat, In-Memory iterator for sequenced based data.
 47
 48    Example
 49    -------
 50    ```py
 51    iterator = Iterator([1, 2, 3])
 52
 53    # Map the results.
 54    for item in iterator.map(lambda item: item * 2):
 55        print(item)
 56    # 2
 57    # 4
 58
 59    # Indexing is also supported.
 60    print(iterator[0])
 61    # 1
 62
 63    # Normal iteration.
 64    for item in iterator:
 65        print(item)
 66    # 1
 67    # 2
 68    # 3
 69
 70    # Union two iterators.
 71    iterator2 = Iterator([4, 5, 6])
 72    final = iterator | iterator2
 73    # <Iterator([1, 2, 3, 4, 5, 6])>
 74    ```
 75
 76    Parameters
 77    ----------
 78    items: `collections.Iterable[Item]`
 79        The items to iterate over.
 80    """
 81
 82    __slots__ = ("_items",)
 83
 84    def __init__(self, items: collections.Iterable[Item]) -> None:
 85        self._items = iter(items)
 86
 87    @typing.overload
 88    def collect(self) -> list[Item]:
 89        ...
 90
 91    @typing.overload
 92    def collect(self, casting: _B) -> list[_B]:
 93        ...
 94
 95    def collect(
 96        self, casting: typing.Optional[_B] = None
 97    ) -> typing.Union[list[Item], list[_B]]:
 98        """Collects all items in the iterator into a list and cast them into an object if provided.
 99
100        Example
101        -------
102        >>> iterator = Iterator([1, 2, 3])
103        >>> iterator.collect(casting=str)
104        ["1", "2", "3"]
105
106        Parameters
107        ----------
108        casting: `T | None`
109            The type to cast the items to. If `None` is provided, the items will be returned as is.
110
111        Raises
112        ------
113        `StopIteration`
114            If no elements are left in the iterator.
115        """
116        if casting is not None:
117            return typing.cast(list[_B], list(map(casting, self._items)))
118
119        return list(self._items)
120
121    def next(self) -> Item:
122        """Returns the next item in the iterator.
123
124        Example
125        -------
126        ```py
127        iterator = Iterator(["1", "2", "3"])
128        item = iterator.next()
129        assert item == "1"
130        item = iterator.next()
131        assert item == "2"
132        ```
133
134        Raises
135        ------
136        `StopIteration`
137            If no elements are left in the iterator.
138        """
139        try:
140            return self.__next__()
141        except StopIteration:
142            self._ok()
143
144    def map(
145        self, predicate: collections.Callable[[Item], OtherItem]
146    ) -> Iterator[OtherItem]:
147        """Maps each item in the iterator to its predicated value.
148
149        Example
150        -------
151        ```py
152        iterator = Iterator(["1", "2", "3"]).map(lambda value: int(value))
153        print(iterator)
154        # <Iterator([1, 2, 3])>
155        ```
156
157        Parameters
158        ----------
159        predicate: `collections.Callable[[Item], OtherItem]`
160            The function to map each item in the iterator to its predicated value.
161
162        Returns
163        -------
164        `Iterator[OtherItem]`
165            The mapped iterator.
166
167        Raises
168        ------
169        `StopIteration`
170            If no elements are left in the iterator.
171        """
172        return Iterator(map(predicate, self._items))
173
174    def take(self, n: int) -> Iterator[Item]:
175        """Take the first number of items until the number of items are yielded or
176        the end of the iterator is reached.
177
178        Example
179        -------
180        ```py
181        iterator = Iterator([GameMode.RAID, GameMode.STRIKE, GameMode.GAMBIT])
182        print(iterator.take(2))
183        # <Iterator([GameMode.RAID, GameMode.STRIKE])>
184        ```
185
186        Parameters
187        ----------
188        n: `int`
189            The number of items to take.
190
191        Raises
192        ------
193        `StopIteration`
194            If no elements are left in the iterator.
195        """
196        return Iterator(itertools.islice(self._items, n))
197
198    def take_while(
199        self, predicate: collections.Callable[[Item], bool]
200    ) -> Iterator[Item]:
201        """Yields items from the iterator while predicate returns `True`.
202
203        Example
204        -------
205        ```py
206        iterator = Iterator([STEAM, XBOX, STADIA])
207        print(iterator.take_while(lambda platform: platform is not XBOX))
208        # <Iterator([STEAM])>
209        ```
210
211        Parameters
212        ----------
213        predicate: `collections.Callable[[Item], bool]`
214            The function to predicate each item in the iterator.
215
216        Raises
217        ------
218        `StopIteration`
219            If no elements are left in the iterator.
220        """
221        return Iterator(itertools.takewhile(predicate, self._items))
222
223    def drop_while(
224        self, predicate: collections.Callable[[Item], bool]
225    ) -> Iterator[Item]:
226        """Yields items from the iterator while predicate returns `False`.
227
228        Example
229        -------
230        ```py
231        iterator = Iterator([DestinyMembership(name="Jim"), DestinyMembership(name="Bob")])
232        print(iterator.drop_while(lambda membership: membership.name is not "Jim"))
233        # <Iterator([DestinyMembership(name="Bob")])>
234        ```
235
236        Parameters
237        ----------
238        predicate: `collections.Callable[[Item], bool]`
239            The function to predicate each item in the iterator.
240
241        Raises
242        ------
243        `StopIteration`
244            If no elements are left in the iterator.
245        """
246        return Iterator(itertools.dropwhile(predicate, self._items))
247
248    def filter(self, predicate: collections.Callable[[Item], bool]) -> Iterator[Item]:
249        """Filters the iterator to only yield items that match the predicate.
250
251        Example
252        -------
253        ```py
254        names = Iterator(["Jim", "Bob", "Mike", "Jess"])
255        print(names.filter(lambda n: n != "Jim"))
256        # <Iterator(["Bob", "Mike", "Jess"])>
257        ```
258        """
259        return Iterator(filter(predicate, self._items))
260
261    def skip(self, n: int) -> Iterator[Item]:
262        """Skips the first number of items in the iterator.
263
264        Example
265        -------
266        ```py
267        iterator = Iterator([STEAM, XBOX, STADIA])
268        print(iterator.skip(1))
269        # <Iterator([XBOX, STADIA])>
270        ```
271        """
272        return Iterator(itertools.islice(self._items, n, None))
273
274    def zip(self, other: Iterator[OtherItem]) -> Iterator[tuple[Item, OtherItem]]:
275        """Zips the iterator with another iterable.
276
277        Example
278        -------
279        ```py
280        iterator = Iterator([1, 3, 5])
281        other = Iterator([2, 4, 6])
282        for item, other_item in iterator.zip(other):
283            print(item, other_item)
284        # <Iterator([(1, 2), (3, 4), (5, 6)])>
285        ```
286
287        Parameters
288        ----------
289        other: `Iterator[OtherItem]`
290            The iterable to zip with.
291
292        Raises
293        ------
294        `StopIteration`
295            If no elements are left in the iterator.
296        """
297        return Iterator(zip(self._items, other))
298
299    def all(self, predicate: collections.Callable[[Item], bool]) -> bool:
300        """`True` if all items in the iterator match the predicate.
301
302        Example
303        -------
304        ```py
305        iterator = Iterator([1, 2, 3])
306        while iterator.all(lambda item: isinstance(item, int)):
307            print("Still all integers")
308            continue
309        # Still all integers
310        ```
311
312        Parameters
313        ----------
314        predicate: `collections.Callable[[Item], bool]`
315            The function to test each item in the iterator.
316
317        Raises
318        ------
319        `StopIteration`
320            If no elements are left in the iterator.
321        """
322        return all(predicate(item) for item in self)
323
324    def any(self, predicate: collections.Callable[[Item], bool]) -> bool:
325        """`True` if any items in the iterator match the predicate.
326
327        Example
328        -------
329        ```py
330        iterator = Iterator([1, 2, 3])
331        if iterator.any(lambda item: isinstance(item, int)):
332            print("At least one item is an int.")
333        # At least one item is an int.
334        ```
335
336        Parameters
337        ----------
338        predicate: `collections.Callable[[Item], bool]`
339            The function to test each item in the iterator.
340
341        Raises
342        ------
343        `StopIteration`
344            If no elements are left in the iterator.
345        """
346        return any(predicate(item) for item in self)
347
348    def sort(
349        self,
350        *,
351        key: collections.Callable[[Item], typeshed.SupportsRichComparison],
352        reverse: bool = False,
353    ) -> Iterator[Item]:
354        """Sorts the iterator.
355
356        Example
357        -------
358        ```py
359        iterator = Iterator([3, 1, 6, 7])
360        print(iterator.sort(key=lambda item: item))
361        # <Iterator([1, 3, 6, 7])>
362        ```
363
364        Parameters
365        ----------
366        key: `collections.Callable[[Item], Any]`
367            The function to sort by.
368        reverse: `bool`
369            Whether to reverse the sort.
370
371        Raises
372        ------
373        `StopIteration`
374            If no elements are left in the iterator.
375        """
376        return Iterator(sorted(self._items, key=key, reverse=reverse))
377
378    def first(self) -> Item:
379        """Returns the first item in the iterator.
380
381        Example
382        -------
383        ```py
384        iterator = Iterator([3, 1, 6, 7])
385        print(iterator.first())
386        3
387        ```
388
389        Raises
390        ------
391        `StopIteration`
392            If no elements are left in the iterator.
393        """
394        return self.take(1).next()
395
396    def reversed(self) -> Iterator[Item]:
397        """Returns a new iterator that yields the items in the iterator in reverse order.
398
399        Example
400        -------
401        ```py
402        iterator = Iterator([3, 1, 6, 7])
403        print(iterator.reversed())
404        # <Iterator([7, 6, 1, 3])>
405        ```
406
407        Raises
408        ------
409        `StopIteration`
410            If no elements are left in the iterator.
411        """
412        return Iterator(reversed(self.collect()))
413
414    def count(self) -> int:
415        """Returns the number of items in the iterator.
416
417        Example
418        -------
419        ```py
420        iterator = Iterator([3, 1, 6, 7])
421        print(iterator.count())
422        4
423        ```
424        """
425        count = 0
426        for _ in self:
427            count += 1
428
429        return count
430
431    def union(self, other: Iterator[Item]) -> Iterator[Item]:
432        """Returns a new iterator that yields all items from both iterators.
433
434        Example
435        -------
436        ```py
437        iterator = Iterator([1, 2, 3])
438        other = Iterator([4, 5, 6])
439        print(iterator.union(other))
440        # <Iterator([1, 2, 3, 4, 5, 6])>
441        ```
442
443        Parameters
444        ----------
445        other: `Iterator[Item]`
446            The iterable to union with.
447
448        Raises
449        ------
450        `StopIteration`
451            If no elements are left in the iterator.
452        """
453        return Iterator(itertools.chain(self._items, other))
454
455    def for_each(self, func: collections.Callable[[Item], typing.Any]) -> None:
456        """Calls the function on each item in the iterator.
457
458        Example
459        -------
460        ```py
461        iterator = Iterator([1, 2, 3])
462        iterator.for_each(lambda item: print(item))
463        # 1
464        # 2
465        # 3
466        ```
467
468        Parameters
469        ----------
470        func: `typeshed.Callable[[Item], None]`
471            The function to call on each item in the iterator.
472        """
473        for item in self:
474            func(item)
475
476    async def async_for_each(
477        self,
478        func: collections.Callable[[Item], collections.Coroutine[None, None, None]],
479    ) -> None:
480        """Calls the async function on each item in the iterator concurrently.
481
482        Example
483        -------
484        ```py
485        async def signup(username: str) -> None:
486            async with aiohttp.request('POST', '...') as r:
487                # Actual logic.
488                ...
489
490        async def main():
491            users = aiobungie.into_iter(["user_danny", "user_jojo"])
492            await users.async_for_each(lambda username: signup(username))
493        ```
494
495        Parameters
496        ----------
497        func: `collections.Callable[[Item], collections.Coroutine[None, None, None]]`
498            The async function to call on each item in the iterator.
499        """
500        await _helpers.awaits(*(func(item) for item in self))
501
502    def enumerate(self, *, start: int = 0) -> Iterator[tuple[int, Item]]:
503        """Returns a new iterator that yields tuples of the index and item.
504
505        Example
506        -------
507        ```py
508        iterator = Iterator([1, 2, 3])
509        for index, item in iterator.enumerate():
510            print(index, item)
511        # 0 1
512        # 1 2
513        # 2 3
514        ```
515
516        Raises
517        ------
518        `StopIteration`
519            If no elements are left in the iterator.
520        """
521        return Iterator(enumerate(self._items, start=start))
522
523    def _ok(self) -> typing.NoReturn:
524        raise StopIteration("No more items in the iterator.") from None
525
526    def __getitem__(self, index: int) -> Item:
527        try:
528            return self.skip(index).first()
529        except IndexError:
530            self._ok()
531
532    def __or__(self, other: Iterator[Item]) -> Iterator[Item]:
533        return self.union(other)
534
535    # This is a never.
536    def __setitem__(self) -> typing.NoReturn:
537        raise TypeError(
538            f"{type(self).__name__} doesn't support item assignment."
539        ) from None
540
541    def __repr__(self) -> str:
542        return f'<{self.__class__.__name__}({", ".join([str(item) for item in self])})>'
543
544    def __len__(self) -> int:
545        return self.count()
546
547    def __iter__(self) -> Iterator[Item]:
548        return self
549
550    def __next__(self) -> Item:
551        try:
552            item = next(self._items)
553        except StopIteration:
554            self._ok()
555
556        return item

A Flat, In-Memory iterator for sequenced based data.

Example
iterator = Iterator([1, 2, 3])

# Map the results.
for item in iterator.map(lambda item: item * 2):
    print(item)
# 2
# 4

# Indexing is also supported.
print(iterator[0])
# 1

# Normal iteration.
for item in iterator:
    print(item)
# 1
# 2
# 3

# Union two iterators.
iterator2 = Iterator([4, 5, 6])
final = iterator | iterator2
# <Iterator([1, 2, 3, 4, 5, 6])>
Parameters
  • items (collections.Iterable[Item]): The items to iterate over.
Iterator(items: collections.abc.Iterable[~Item])
84    def __init__(self, items: collections.Iterable[Item]) -> None:
85        self._items = iter(items)
def collect( self, casting: 'typing.Optional[_B]' = None) -> 'typing.Union[list[Item], list[_B]]':
 95    def collect(
 96        self, casting: typing.Optional[_B] = None
 97    ) -> typing.Union[list[Item], list[_B]]:
 98        """Collects all items in the iterator into a list and cast them into an object if provided.
 99
100        Example
101        -------
102        >>> iterator = Iterator([1, 2, 3])
103        >>> iterator.collect(casting=str)
104        ["1", "2", "3"]
105
106        Parameters
107        ----------
108        casting: `T | None`
109            The type to cast the items to. If `None` is provided, the items will be returned as is.
110
111        Raises
112        ------
113        `StopIteration`
114            If no elements are left in the iterator.
115        """
116        if casting is not None:
117            return typing.cast(list[_B], list(map(casting, self._items)))
118
119        return list(self._items)

Collects all items in the iterator into a list and cast them into an object if provided.

Example
>>> iterator = Iterator([1, 2, 3])
>>> iterator.collect(casting=str)
["1", "2", "3"]
Parameters
  • casting (T | None): The type to cast the items to. If None is provided, the items will be returned as is.
Raises
  • StopIteration: If no elements are left in the iterator.
def next(self) -> ~Item:
121    def next(self) -> Item:
122        """Returns the next item in the iterator.
123
124        Example
125        -------
126        ```py
127        iterator = Iterator(["1", "2", "3"])
128        item = iterator.next()
129        assert item == "1"
130        item = iterator.next()
131        assert item == "2"
132        ```
133
134        Raises
135        ------
136        `StopIteration`
137            If no elements are left in the iterator.
138        """
139        try:
140            return self.__next__()
141        except StopIteration:
142            self._ok()

Returns the next item in the iterator.

Example
iterator = Iterator(["1", "2", "3"])
item = iterator.next()
assert item == "1"
item = iterator.next()
assert item == "2"
Raises
  • StopIteration: If no elements are left in the iterator.
def map( self, predicate: 'collections.Callable[[Item], OtherItem]') -> 'Iterator[OtherItem]':
144    def map(
145        self, predicate: collections.Callable[[Item], OtherItem]
146    ) -> Iterator[OtherItem]:
147        """Maps each item in the iterator to its predicated value.
148
149        Example
150        -------
151        ```py
152        iterator = Iterator(["1", "2", "3"]).map(lambda value: int(value))
153        print(iterator)
154        # <Iterator([1, 2, 3])>
155        ```
156
157        Parameters
158        ----------
159        predicate: `collections.Callable[[Item], OtherItem]`
160            The function to map each item in the iterator to its predicated value.
161
162        Returns
163        -------
164        `Iterator[OtherItem]`
165            The mapped iterator.
166
167        Raises
168        ------
169        `StopIteration`
170            If no elements are left in the iterator.
171        """
172        return Iterator(map(predicate, self._items))

Maps each item in the iterator to its predicated value.

Example
iterator = Iterator(["1", "2", "3"]).map(lambda value: int(value))
print(iterator)
# <Iterator([1, 2, 3])>
Parameters
  • predicate (collections.Callable[[Item], OtherItem]): The function to map each item in the iterator to its predicated value.
Returns
  • Iterator[OtherItem]: The mapped iterator.
Raises
  • StopIteration: If no elements are left in the iterator.
def take(self, n: int) -> aiobungie.Iterator[~Item]:
174    def take(self, n: int) -> Iterator[Item]:
175        """Take the first number of items until the number of items are yielded or
176        the end of the iterator is reached.
177
178        Example
179        -------
180        ```py
181        iterator = Iterator([GameMode.RAID, GameMode.STRIKE, GameMode.GAMBIT])
182        print(iterator.take(2))
183        # <Iterator([GameMode.RAID, GameMode.STRIKE])>
184        ```
185
186        Parameters
187        ----------
188        n: `int`
189            The number of items to take.
190
191        Raises
192        ------
193        `StopIteration`
194            If no elements are left in the iterator.
195        """
196        return Iterator(itertools.islice(self._items, n))

Take the first number of items until the number of items are yielded or the end of the iterator is reached.

Example
iterator = Iterator([GameMode.RAID, GameMode.STRIKE, GameMode.GAMBIT])
print(iterator.take(2))
# <Iterator([GameMode.RAID, GameMode.STRIKE])>
Parameters
  • n (int): The number of items to take.
Raises
  • StopIteration: If no elements are left in the iterator.
def take_while( self, predicate: collections.abc.Callable[[~Item], bool]) -> aiobungie.Iterator[~Item]:
198    def take_while(
199        self, predicate: collections.Callable[[Item], bool]
200    ) -> Iterator[Item]:
201        """Yields items from the iterator while predicate returns `True`.
202
203        Example
204        -------
205        ```py
206        iterator = Iterator([STEAM, XBOX, STADIA])
207        print(iterator.take_while(lambda platform: platform is not XBOX))
208        # <Iterator([STEAM])>
209        ```
210
211        Parameters
212        ----------
213        predicate: `collections.Callable[[Item], bool]`
214            The function to predicate each item in the iterator.
215
216        Raises
217        ------
218        `StopIteration`
219            If no elements are left in the iterator.
220        """
221        return Iterator(itertools.takewhile(predicate, self._items))

Yields items from the iterator while predicate returns True.

Example
iterator = Iterator([STEAM, XBOX, STADIA])
print(iterator.take_while(lambda platform: platform is not XBOX))
# <Iterator([STEAM])>
Parameters
  • predicate (collections.Callable[[Item], bool]): The function to predicate each item in the iterator.
Raises
  • StopIteration: If no elements are left in the iterator.
def drop_while( self, predicate: collections.abc.Callable[[~Item], bool]) -> aiobungie.Iterator[~Item]:
223    def drop_while(
224        self, predicate: collections.Callable[[Item], bool]
225    ) -> Iterator[Item]:
226        """Yields items from the iterator while predicate returns `False`.
227
228        Example
229        -------
230        ```py
231        iterator = Iterator([DestinyMembership(name="Jim"), DestinyMembership(name="Bob")])
232        print(iterator.drop_while(lambda membership: membership.name is not "Jim"))
233        # <Iterator([DestinyMembership(name="Bob")])>
234        ```
235
236        Parameters
237        ----------
238        predicate: `collections.Callable[[Item], bool]`
239            The function to predicate each item in the iterator.
240
241        Raises
242        ------
243        `StopIteration`
244            If no elements are left in the iterator.
245        """
246        return Iterator(itertools.dropwhile(predicate, self._items))

Yields items from the iterator while predicate returns False.

Example
iterator = Iterator([DestinyMembership(name="Jim"), DestinyMembership(name="Bob")])
print(iterator.drop_while(lambda membership: membership.name is not "Jim"))
# <Iterator([DestinyMembership(name="Bob")])>
Parameters
  • predicate (collections.Callable[[Item], bool]): The function to predicate each item in the iterator.
Raises
  • StopIteration: If no elements are left in the iterator.
def filter( self, predicate: collections.abc.Callable[[~Item], bool]) -> aiobungie.Iterator[~Item]:
248    def filter(self, predicate: collections.Callable[[Item], bool]) -> Iterator[Item]:
249        """Filters the iterator to only yield items that match the predicate.
250
251        Example
252        -------
253        ```py
254        names = Iterator(["Jim", "Bob", "Mike", "Jess"])
255        print(names.filter(lambda n: n != "Jim"))
256        # <Iterator(["Bob", "Mike", "Jess"])>
257        ```
258        """
259        return Iterator(filter(predicate, self._items))

Filters the iterator to only yield items that match the predicate.

Example
names = Iterator(["Jim", "Bob", "Mike", "Jess"])
print(names.filter(lambda n: n != "Jim"))
# <Iterator(["Bob", "Mike", "Jess"])>
def skip(self, n: int) -> aiobungie.Iterator[~Item]:
261    def skip(self, n: int) -> Iterator[Item]:
262        """Skips the first number of items in the iterator.
263
264        Example
265        -------
266        ```py
267        iterator = Iterator([STEAM, XBOX, STADIA])
268        print(iterator.skip(1))
269        # <Iterator([XBOX, STADIA])>
270        ```
271        """
272        return Iterator(itertools.islice(self._items, n, None))

Skips the first number of items in the iterator.

Example
iterator = Iterator([STEAM, XBOX, STADIA])
print(iterator.skip(1))
# <Iterator([XBOX, STADIA])>
def zip(self, other: 'Iterator[OtherItem]') -> 'Iterator[tuple[Item, OtherItem]]':
274    def zip(self, other: Iterator[OtherItem]) -> Iterator[tuple[Item, OtherItem]]:
275        """Zips the iterator with another iterable.
276
277        Example
278        -------
279        ```py
280        iterator = Iterator([1, 3, 5])
281        other = Iterator([2, 4, 6])
282        for item, other_item in iterator.zip(other):
283            print(item, other_item)
284        # <Iterator([(1, 2), (3, 4), (5, 6)])>
285        ```
286
287        Parameters
288        ----------
289        other: `Iterator[OtherItem]`
290            The iterable to zip with.
291
292        Raises
293        ------
294        `StopIteration`
295            If no elements are left in the iterator.
296        """
297        return Iterator(zip(self._items, other))

Zips the iterator with another iterable.

Example
iterator = Iterator([1, 3, 5])
other = Iterator([2, 4, 6])
for item, other_item in iterator.zip(other):
    print(item, other_item)
# <Iterator([(1, 2), (3, 4), (5, 6)])>
Parameters
  • other (Iterator[OtherItem]): The iterable to zip with.
Raises
  • StopIteration: If no elements are left in the iterator.
def all(self, predicate: collections.abc.Callable[[~Item], bool]) -> bool:
299    def all(self, predicate: collections.Callable[[Item], bool]) -> bool:
300        """`True` if all items in the iterator match the predicate.
301
302        Example
303        -------
304        ```py
305        iterator = Iterator([1, 2, 3])
306        while iterator.all(lambda item: isinstance(item, int)):
307            print("Still all integers")
308            continue
309        # Still all integers
310        ```
311
312        Parameters
313        ----------
314        predicate: `collections.Callable[[Item], bool]`
315            The function to test each item in the iterator.
316
317        Raises
318        ------
319        `StopIteration`
320            If no elements are left in the iterator.
321        """
322        return all(predicate(item) for item in self)

True if all items in the iterator match the predicate.

Example
iterator = Iterator([1, 2, 3])
while iterator.all(lambda item: isinstance(item, int)):
    print("Still all integers")
    continue
# Still all integers
Parameters
  • predicate (collections.Callable[[Item], bool]): The function to test each item in the iterator.
Raises
  • StopIteration: If no elements are left in the iterator.
def any(self, predicate: collections.abc.Callable[[~Item], bool]) -> bool:
324    def any(self, predicate: collections.Callable[[Item], bool]) -> bool:
325        """`True` if any items in the iterator match the predicate.
326
327        Example
328        -------
329        ```py
330        iterator = Iterator([1, 2, 3])
331        if iterator.any(lambda item: isinstance(item, int)):
332            print("At least one item is an int.")
333        # At least one item is an int.
334        ```
335
336        Parameters
337        ----------
338        predicate: `collections.Callable[[Item], bool]`
339            The function to test each item in the iterator.
340
341        Raises
342        ------
343        `StopIteration`
344            If no elements are left in the iterator.
345        """
346        return any(predicate(item) for item in self)

True if any items in the iterator match the predicate.

Example
iterator = Iterator([1, 2, 3])
if iterator.any(lambda item: isinstance(item, int)):
    print("At least one item is an int.")
# At least one item is an int.
Parameters
  • predicate (collections.Callable[[Item], bool]): The function to test each item in the iterator.
Raises
  • StopIteration: If no elements are left in the iterator.
def sort( self, *, key: 'collections.Callable[[Item], typeshed.SupportsRichComparison]', reverse: bool = False) -> aiobungie.Iterator[~Item]:
348    def sort(
349        self,
350        *,
351        key: collections.Callable[[Item], typeshed.SupportsRichComparison],
352        reverse: bool = False,
353    ) -> Iterator[Item]:
354        """Sorts the iterator.
355
356        Example
357        -------
358        ```py
359        iterator = Iterator([3, 1, 6, 7])
360        print(iterator.sort(key=lambda item: item))
361        # <Iterator([1, 3, 6, 7])>
362        ```
363
364        Parameters
365        ----------
366        key: `collections.Callable[[Item], Any]`
367            The function to sort by.
368        reverse: `bool`
369            Whether to reverse the sort.
370
371        Raises
372        ------
373        `StopIteration`
374            If no elements are left in the iterator.
375        """
376        return Iterator(sorted(self._items, key=key, reverse=reverse))

Sorts the iterator.

Example
iterator = Iterator([3, 1, 6, 7])
print(iterator.sort(key=lambda item: item))
# <Iterator([1, 3, 6, 7])>
Parameters
  • key (collections.Callable[[Item], Any]): The function to sort by.
  • reverse (bool): Whether to reverse the sort.
Raises
  • StopIteration: If no elements are left in the iterator.
def first(self) -> ~Item:
378    def first(self) -> Item:
379        """Returns the first item in the iterator.
380
381        Example
382        -------
383        ```py
384        iterator = Iterator([3, 1, 6, 7])
385        print(iterator.first())
386        3
387        ```
388
389        Raises
390        ------
391        `StopIteration`
392            If no elements are left in the iterator.
393        """
394        return self.take(1).next()

Returns the first item in the iterator.

Example
iterator = Iterator([3, 1, 6, 7])
print(iterator.first())
3
Raises
  • StopIteration: If no elements are left in the iterator.
def reversed(self) -> aiobungie.Iterator[~Item]:
396    def reversed(self) -> Iterator[Item]:
397        """Returns a new iterator that yields the items in the iterator in reverse order.
398
399        Example
400        -------
401        ```py
402        iterator = Iterator([3, 1, 6, 7])
403        print(iterator.reversed())
404        # <Iterator([7, 6, 1, 3])>
405        ```
406
407        Raises
408        ------
409        `StopIteration`
410            If no elements are left in the iterator.
411        """
412        return Iterator(reversed(self.collect()))

Returns a new iterator that yields the items in the iterator in reverse order.

Example
iterator = Iterator([3, 1, 6, 7])
print(iterator.reversed())
# <Iterator([7, 6, 1, 3])>
Raises
  • StopIteration: If no elements are left in the iterator.
def count(self) -> int:
414    def count(self) -> int:
415        """Returns the number of items in the iterator.
416
417        Example
418        -------
419        ```py
420        iterator = Iterator([3, 1, 6, 7])
421        print(iterator.count())
422        4
423        ```
424        """
425        count = 0
426        for _ in self:
427            count += 1
428
429        return count

Returns the number of items in the iterator.

Example
iterator = Iterator([3, 1, 6, 7])
print(iterator.count())
4
def union( self, other: aiobungie.Iterator[~Item]) -> aiobungie.Iterator[~Item]:
431    def union(self, other: Iterator[Item]) -> Iterator[Item]:
432        """Returns a new iterator that yields all items from both iterators.
433
434        Example
435        -------
436        ```py
437        iterator = Iterator([1, 2, 3])
438        other = Iterator([4, 5, 6])
439        print(iterator.union(other))
440        # <Iterator([1, 2, 3, 4, 5, 6])>
441        ```
442
443        Parameters
444        ----------
445        other: `Iterator[Item]`
446            The iterable to union with.
447
448        Raises
449        ------
450        `StopIteration`
451            If no elements are left in the iterator.
452        """
453        return Iterator(itertools.chain(self._items, other))

Returns a new iterator that yields all items from both iterators.

Example
iterator = Iterator([1, 2, 3])
other = Iterator([4, 5, 6])
print(iterator.union(other))
# <Iterator([1, 2, 3, 4, 5, 6])>
Parameters
  • other (Iterator[Item]): The iterable to union with.
Raises
  • StopIteration: If no elements are left in the iterator.
def for_each(self, func: collections.abc.Callable[[~Item], typing.Any]) -> None:
455    def for_each(self, func: collections.Callable[[Item], typing.Any]) -> None:
456        """Calls the function on each item in the iterator.
457
458        Example
459        -------
460        ```py
461        iterator = Iterator([1, 2, 3])
462        iterator.for_each(lambda item: print(item))
463        # 1
464        # 2
465        # 3
466        ```
467
468        Parameters
469        ----------
470        func: `typeshed.Callable[[Item], None]`
471            The function to call on each item in the iterator.
472        """
473        for item in self:
474            func(item)

Calls the function on each item in the iterator.

Example
iterator = Iterator([1, 2, 3])
iterator.for_each(lambda item: print(item))
# 1
# 2
# 3
Parameters
  • func (typeshed.Callable[[Item], None]): The function to call on each item in the iterator.
async def async_for_each( self, func: collections.abc.Callable[[~Item], collections.abc.Coroutine[None, None, None]]) -> None:
476    async def async_for_each(
477        self,
478        func: collections.Callable[[Item], collections.Coroutine[None, None, None]],
479    ) -> None:
480        """Calls the async function on each item in the iterator concurrently.
481
482        Example
483        -------
484        ```py
485        async def signup(username: str) -> None:
486            async with aiohttp.request('POST', '...') as r:
487                # Actual logic.
488                ...
489
490        async def main():
491            users = aiobungie.into_iter(["user_danny", "user_jojo"])
492            await users.async_for_each(lambda username: signup(username))
493        ```
494
495        Parameters
496        ----------
497        func: `collections.Callable[[Item], collections.Coroutine[None, None, None]]`
498            The async function to call on each item in the iterator.
499        """
500        await _helpers.awaits(*(func(item) for item in self))

Calls the async function on each item in the iterator concurrently.

Example
async def signup(username: str) -> None:
    async with aiohttp.request('POST', '...') as r:
        # Actual logic.
        ...

async def main():
    users = aiobungie.into_iter(["user_danny", "user_jojo"])
    await users.async_for_each(lambda username: signup(username))
Parameters
  • func (collections.Callable[[Item], collections.Coroutine[None, None, None]]): The async function to call on each item in the iterator.
def enumerate( self, *, start: int = 0) -> aiobungie.Iterator[tuple[int, ~Item]]:
502    def enumerate(self, *, start: int = 0) -> Iterator[tuple[int, Item]]:
503        """Returns a new iterator that yields tuples of the index and item.
504
505        Example
506        -------
507        ```py
508        iterator = Iterator([1, 2, 3])
509        for index, item in iterator.enumerate():
510            print(index, item)
511        # 0 1
512        # 1 2
513        # 2 3
514        ```
515
516        Raises
517        ------
518        `StopIteration`
519            If no elements are left in the iterator.
520        """
521        return Iterator(enumerate(self._items, start=start))

Returns a new iterator that yields tuples of the index and item.

Example
iterator = Iterator([1, 2, 3])
for index, item in iterator.enumerate():
    print(index, item)
# 0 1
# 1 2
# 2 3
Raises
  • StopIteration: If no elements are left in the iterator.
@typing.final
class MembershipOption(builtins.int, aiobungie.Enum):
710@typing.final
711class MembershipOption(int, Enum):
712    """A enum for GroupV2 membership options."""
713
714    REVIEWD = 0
715    OPEN = 1
716    CLOSED = 2

A enum for GroupV2 membership options.

REVIEWD = <MembershipOption.REVIEWD: 0>
OPEN = <MembershipOption.OPEN: 1>
CLOSED = <MembershipOption.CLOSED: 2>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class MembershipType(builtins.int, aiobungie.Enum):
458@typing.final
459class MembershipType(int, Enum):
460    """An Enum for Bungie membership types."""
461
462    NONE = 0
463    XBOX = 1
464    PSN = 2
465    STEAM = 3
466    BLIZZARD = 4
467    STADIA = 5
468    EPIC_GAMES_STORE = 6
469    DEMON = 10
470    BUNGIE = 254
471    ALL = -1

An Enum for Bungie membership types.

NONE = <MembershipType.NONE: 0>
XBOX = <MembershipType.XBOX: 1>
PSN = <MembershipType.PSN: 2>
STEAM = <MembershipType.STEAM: 3>
BLIZZARD = <MembershipType.BLIZZARD: 4>
STADIA = <MembershipType.STADIA: 5>
EPIC_GAMES_STORE = <MembershipType.EPIC_GAMES_STORE: 6>
DEMON = <MembershipType.DEMON: 10>
BUNGIE = <MembershipType.BUNGIE: 254>
ALL = <MembershipType.ALL: -1>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@attrs.define(auto_exc=True)
class MembershipTypeError(aiobungie.BadRequest):
163@attrs.define(auto_exc=True)
164class MembershipTypeError(BadRequest):
165    """A bad request error raised when passing wrong membership to the request.
166
167    Those fields are useful since it returns the correct membership and id which can be used
168    to make the request again with those fields.
169    """
170
171    membership_type: str = attrs.field(default="")
172    """The errored membership type passed to the request."""
173
174    membership_id: int = attrs.field(default=0)
175    """The errored user's membership id."""
176
177    required_membership: str = attrs.field(default="")
178    """The required correct membership for errored user."""
179
180    def __str__(self) -> str:
181        return (
182            f"Expected membership: {self.required_membership}, "
183            f"But got {self.membership_type} for id {self.membership_id}"
184        )
185
186    def __int__(self) -> int:
187        return int(self.membership_id)

A bad request error raised when passing wrong membership to the request.

Those fields are useful since it returns the correct membership and id which can be used to make the request again with those fields.

MembershipTypeError( message: str, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str], http_status: http.HTTPStatus = <HTTPStatus.BAD_REQUEST: 400>, membership_type: str = '', membership_id: int = 0, required_membership: str = '')
 2def __init__(self, message, url, body, headers, http_status=attr_dict['http_status'].default, membership_type=attr_dict['membership_type'].default, membership_id=attr_dict['membership_id'].default, required_membership=attr_dict['required_membership'].default):
 3    self.message = message
 4    self.url = url
 5    self.body = body
 6    self.headers = headers
 7    self.http_status = http_status
 8    self.membership_type = membership_type
 9    self.membership_id = membership_id
10    self.required_membership = required_membership
11    BaseException.__init__(self, self.message,self.url,self.body,self.headers,self.http_status,self.membership_type,self.membership_id,self.required_membership)

Method generated by attrs for class MembershipTypeError.

membership_type: str

The errored membership type passed to the request.

membership_id: int

The errored user's membership id.

required_membership: str

The required correct membership for errored user.

Inherited Members
BadRequest
url
body
headers
http_status
HTTPError
message
builtins.BaseException
with_traceback
@typing.final
class MilestoneType(builtins.int, aiobungie.Enum):
503@typing.final
504class MilestoneType(int, Enum):
505    """An Enum for Destiny 2 milestone types."""
506
507    UNKNOWN = 0
508    TUTORIAL = 1
509    ONETIME = 2
510    WEEKLY = 3
511    DAILY = 4
512    SPECIAL = 5

An Enum for Destiny 2 milestone types.

UNKNOWN = <MilestoneType.UNKNOWN: 0>
TUTORIAL = <MilestoneType.TUTORIAL: 1>
ONETIME = <MilestoneType.ONETIME: 2>
WEEKLY = <MilestoneType.WEEKLY: 3>
DAILY = <MilestoneType.DAILY: 4>
SPECIAL = <MilestoneType.SPECIAL: 5>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@attrs.define(auto_exc=True)
class NotFound(aiobungie.HTTPException):
129@attrs.define(auto_exc=True)
130class NotFound(HTTPException):
131    """Raised when an unknown request was not found."""
132
133    http_status: http.HTTPStatus = attrs.field(
134        default=http.HTTPStatus.NOT_FOUND, init=False
135    )

Raised when an unknown request was not found.

NotFound( *, error_code: int, throttle_seconds: int, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str], message: str, error_status: str, message_data: dict[str, str])
 2def __init__(self, *, error_code, throttle_seconds, url, body, headers, message, error_status, message_data):
 3    self.error_code = error_code
 4    self.throttle_seconds = throttle_seconds
 5    self.url = url
 6    self.body = body
 7    self.headers = headers
 8    self.message = message
 9    self.error_status = error_status
10    self.message_data = message_data
11    self.http_status = attr_dict['http_status'].default
12    BaseException.__init__(self, self.error_code,self.throttle_seconds,self.url,self.body,self.headers,self.message,self.error_status,self.message_data)

Method generated by attrs for class NotFound.

http_status: http.HTTPStatus

The request response http status.

Inherited Members
HTTPException
error_code
throttle_seconds
url
body
headers
message
error_status
message_data
builtins.BaseException
with_traceback
@typing.final
class ObjectiveUIStyle(builtins.int, aiobungie.Enum):
 94@typing.final
 95class ObjectiveUIStyle(int, enums.Enum):
 96    NONE = 0
 97    HIGHLIGHTED = 1
 98    CRAFTING_WEAPON_LEVEL = 2
 99    CRAFTING_WEAPON_LEVEL_PROGRESS = 3
100    CRAFTING_WEAPON_TIMESTAMP = 4
101    CRAFTING_MEMENTOS = 5
102    CRAFTING_MEMENTO_TITLE = 6

An enumeration.

NONE = <ObjectiveUIStyle.NONE: 0>
HIGHLIGHTED = <ObjectiveUIStyle.HIGHLIGHTED: 1>
CRAFTING_WEAPON_LEVEL = <ObjectiveUIStyle.CRAFTING_WEAPON_LEVEL: 2>
CRAFTING_WEAPON_LEVEL_PROGRESS = <ObjectiveUIStyle.CRAFTING_WEAPON_LEVEL_PROGRESS: 3>
CRAFTING_WEAPON_TIMESTAMP = <ObjectiveUIStyle.CRAFTING_WEAPON_TIMESTAMP: 4>
CRAFTING_MEMENTOS = <ObjectiveUIStyle.CRAFTING_MEMENTOS: 5>
CRAFTING_MEMENTO_TITLE = <ObjectiveUIStyle.CRAFTING_MEMENTO_TITLE: 6>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Place(builtins.int, aiobungie.Enum):
230@typing.final
231class Place(int, Enum):
232    """An Enum for Destiny 2 Places and NOT Planets"""
233
234    ORBIT = 2961497387
235    SOCIAL = 4151112093
236    LIGHT_HOUSE = 4276116472
237    EXPLORE = 3497767639

An Enum for Destiny 2 Places and NOT Planets

ORBIT = <Place.ORBIT: 2961497387>
SOCIAL = <Place.SOCIAL: 4151112093>
LIGHT_HOUSE = <Place.LIGHT_HOUSE: 4276116472>
EXPLORE = <Place.EXPLORE: 3497767639>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Planet(builtins.int, aiobungie.Enum):
195@typing.final
196class Planet(int, Enum):
197    """An Enum for all available planets in Destiny 2."""
198
199    UNKNOWN = 0
200    """Unknown space"""
201
202    EARTH = 3747705955
203    """Earth"""
204
205    DREAMING_CITY = 2877881518
206    """The Dreaming city."""
207
208    NESSUS = 3526908984
209    """Nessus"""
210
211    MOON = 3325508439
212    """The Moon"""
213
214    COSMODROME = 3990611421
215    """The Cosmodrome"""
216
217    TANGLED_SHORE = 3821439926
218    """The Tangled Shore"""
219
220    VENUS = 3871070152
221    """Venus"""
222
223    EAZ = 541863059  # Exclusive event.
224    """European Aerial Zone"""
225
226    EUROPA = 1729879943
227    """Europa"""

An Enum for all available planets in Destiny 2.

UNKNOWN = <Planet.UNKNOWN: 0>

Unknown space

EARTH = <Planet.EARTH: 3747705955>

Earth

DREAMING_CITY = <Planet.DREAMING_CITY: 2877881518>

The Dreaming city.

NESSUS = <Planet.NESSUS: 3526908984>

Nessus

MOON = <Planet.MOON: 3325508439>

The Moon

COSMODROME = <Planet.COSMODROME: 3990611421>

The Cosmodrome

TANGLED_SHORE = <Planet.TANGLED_SHORE: 3821439926>

The Tangled Shore

VENUS = <Planet.VENUS: 3871070152>

Venus

EAZ = <Planet.EAZ: 541863059>

European Aerial Zone

EUROPA = <Planet.EUROPA: 1729879943>

Europa

Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Presence(builtins.int, aiobungie.Enum):
680@typing.final
681class Presence(int, Enum):
682    """An enum for a bungie friend status."""
683
684    OFFLINE_OR_UNKNOWN = 0
685    ONLINE = 1

An enum for a bungie friend status.

OFFLINE_OR_UNKNOWN = <Presence.OFFLINE_OR_UNKNOWN: 0>
ONLINE = <Presence.ONLINE: 1>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class PrivacySetting(builtins.int, aiobungie.Enum):
768@typing.final
769class PrivacySetting(int, Enum):
770    """An enum for players's privacy settings."""
771
772    OPEN = 0
773    CLAN_AND_FRIENDS = 1
774    FRIENDS_ONLY = 2
775    INVITE_ONLY = 3
776    CLOSED = 4

An enum for players's privacy settings.

OPEN = <PrivacySetting.OPEN: 0>
CLAN_AND_FRIENDS = <PrivacySetting.CLAN_AND_FRIENDS: 1>
FRIENDS_ONLY = <PrivacySetting.FRIENDS_ONLY: 2>
INVITE_ONLY = <PrivacySetting.INVITE_ONLY: 3>
CLOSED = <PrivacySetting.CLOSED: 4>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
class RESTClient(aiobungie.interfaces.rest.RESTInterface):
 362class RESTClient(interfaces.RESTInterface):
 363    """A RESTful client implementation for Bungie's API.
 364
 365    This client is designed to only make HTTP requests and return JSON objects
 366    to provide RESTful functionality.
 367
 368    This client is also used within `aiobungie.Client` which deserialize those returned JSON objects
 369    using the factory into Pythonic data classes objects which provide Python functionality.
 370
 371    Example
 372    -------
 373    ```py
 374    import aiobungie
 375
 376    async def main():
 377        async with aiobungie.RESTClient("TOKEN") as rest_client:
 378            req = await rest_client.fetch_clan_members(4389205)
 379            clan_members = req['results']
 380            for member in clan_members:
 381                for k, v in member['destinyUserInfo'].items():
 382                    print(k, v)
 383    ```
 384
 385    Parameters
 386    ----------
 387    token : `str`
 388        A valid application token from Bungie's developer portal.
 389
 390    Other Parameters
 391    ----------------
 392    max_retries : `int`
 393        The max retries number to retry if the request hit a `5xx` status code.
 394    max_ratelimit_retries : `int`
 395        The max retries number to retry if the request hit a `429` status code. Defaults to `3`.
 396    client_secret : `typing.Optional[str]`
 397        An optional application client secret,
 398        This is only needed if you're fetching OAuth2 tokens with this client.
 399    client_id : `typing.Optional[int]`
 400        An optional application client id,
 401        This is only needed if you're fetching OAuth2 tokens with this client.
 402    enable_debugging : `bool | str`
 403        Whether to enable logging responses or not.
 404
 405    Logging Levels
 406    --------------
 407    * `False`: This will disable logging.
 408    * `True`: This will set the level to `DEBUG` and enable logging minimal information.
 409    * `"TRACE" | aiobungie.TRACE`: This will log the response headers along with the minimal information.
 410    """
 411
 412    __slots__ = (
 413        "_token",
 414        "_session",
 415        "_lock",
 416        "_max_retries",
 417        "_client_secret",
 418        "_client_id",
 419        "_metadata",
 420        "_max_rate_limit_retries",
 421    )
 422
 423    def __init__(
 424        self,
 425        token: str,
 426        /,
 427        client_secret: typing.Optional[str] = None,
 428        client_id: typing.Optional[int] = None,
 429        *,
 430        max_retries: int = 4,
 431        max_ratelimit_retries: int = 3,
 432        enable_debugging: typing.Union[typing.Literal["TRACE"], bool, int] = False,
 433    ) -> None:
 434        self._session: typing.Optional[_Session] = None
 435        self._lock: typing.Optional[asyncio.Lock] = None
 436        self._client_secret = client_secret
 437        self._client_id = client_id
 438        self._token: str = token
 439        self._max_retries = max_retries
 440        self._max_rate_limit_retries = max_ratelimit_retries
 441        self._metadata: collections.MutableMapping[typing.Any, typing.Any] = {}
 442
 443        self._set_debug_level(enable_debugging)
 444
 445    @property
 446    def client_id(self) -> typing.Optional[int]:
 447        return self._client_id
 448
 449    @property
 450    def metadata(self) -> collections.MutableMapping[typing.Any, typing.Any]:
 451        return self._metadata
 452
 453    @property
 454    def is_alive(self) -> bool:
 455        return self._session is not None
 456
 457    @typing.final
 458    async def close(self) -> None:
 459        session = self._get_session()
 460        await session.close()
 461        self._session = None
 462
 463    @typing.final
 464    def open(self) -> None:
 465        """Open a new client session. This is called internally with contextmanager usage."""
 466        if self.is_alive:
 467            raise RuntimeError("Cannot open a new session while it's already open.")
 468
 469        self._session = _Session.create(owner=False, raise_status=False)
 470
 471    @typing.final
 472    def enable_debugging(
 473        self,
 474        level: typing.Union[typing.Literal["TRACE"], bool, int] = False,
 475        file: typing.Optional[typing.Union[pathlib.Path, str]] = None,
 476        /,
 477    ) -> None:
 478        self._set_debug_level(level, file)
 479
 480    @typing.final
 481    async def static_request(
 482        self,
 483        method: typing.Union[RequestMethod, str],
 484        path: str,
 485        *,
 486        auth: typing.Optional[str] = None,
 487        json: typing.Optional[dict[str, typing.Any]] = None,
 488    ) -> ResponseSig:
 489        return await self._request(method, path, auth=auth, json=json)
 490
 491    @typing.final
 492    def build_oauth2_url(
 493        self, client_id: typing.Optional[int] = None
 494    ) -> typing.Optional[builders.OAuthURL]:
 495        client_id = client_id or self._client_id
 496        if client_id is None:
 497            return None
 498
 499        return builders.OAuthURL(client_id=client_id)
 500
 501    @staticmethod
 502    def _set_debug_level(
 503        level: typing.Union[typing.Literal["TRACE"], bool, int] = False,
 504        file: typing.Optional[typing.Union[pathlib.Path, str]] = None,
 505    ) -> None:
 506
 507        file_handler = logging.FileHandler(file, mode="w") if file else None
 508        if level == "TRACE" or level == TRACE:
 509            logging.basicConfig(
 510                level=TRACE, handlers=[file_handler] if file_handler else None
 511            )
 512
 513        elif level:
 514            logging.basicConfig(
 515                level=logging.DEBUG, handlers=[file_handler] if file_handler else None
 516            )
 517
 518    def _get_session(self) -> _Session:
 519        if self._session:
 520            return self._session
 521
 522        raise RuntimeError(
 523            "Cannot return a session while its close. Make sure you use `async with` before making requests."
 524        )
 525
 526    async def _request(
 527        self,
 528        method: typing.Union[RequestMethod, str],
 529        route: str,
 530        *,
 531        base: bool = False,
 532        oauth2: bool = False,
 533        auth: typing.Optional[str] = None,
 534        unwrapping: typing.Literal["json", "read"] = "json",
 535        json: typing.Optional[dict[str, typing.Any]] = None,
 536        headers: typing.Optional[dict[str, typing.Any]] = None,
 537        data: typing.Optional[typing.Union[str, dict[str, typing.Any]]] = None,
 538    ) -> ResponseSig:
 539
 540        retries: int = 0
 541        session = self._get_session()
 542        headers = headers or {}
 543
 544        headers.setdefault(_USER_AGENT_HEADERS, _USER_AGENT)
 545        headers["X-API-KEY"] = self._token
 546
 547        if auth is not None:
 548            headers[_AUTH_HEADER] = f"Bearer {auth}"
 549
 550        # Handling endpoints
 551        endpoint = url.BASE
 552
 553        if not base:
 554            endpoint = endpoint + url.REST_EP
 555
 556        if oauth2:
 557            headers["Content-Type"] = "application/x-www-form-urlencoded"
 558            endpoint = endpoint + url.TOKEN_EP
 559
 560        if self._lock is None:
 561            self._lock = asyncio.Lock()
 562
 563        while True:
 564            try:
 565                async with (stack := contextlib.AsyncExitStack()):
 566                    await stack.enter_async_context(self._lock)
 567
 568                    # We make the request here.
 569                    taken_time = time.monotonic()
 570                    response = await stack.enter_async_context(
 571                        session.client_session.request(
 572                            method=method,
 573                            url=f"{endpoint}/{route}",
 574                            json=json,
 575                            headers=headers,
 576                            data=data,
 577                        )
 578                    )
 579                    response_time = (time.monotonic() - taken_time) * 1_000
 580
 581                    _LOG.debug(
 582                        "%s %s %s Time %.4fms",
 583                        method,
 584                        f"{endpoint}/{route}",
 585                        f"{response.status} {response.reason}",
 586                        response_time,
 587                    )
 588
 589                    await self._handle_ratelimit(
 590                        response, method, route, self._max_rate_limit_retries
 591                    )
 592
 593                    if response.status == http.HTTPStatus.NO_CONTENT:
 594                        return None
 595
 596                    if 300 > response.status >= 200:
 597                        if unwrapping == "read":
 598                            # We need to read the bytes for the manifest response.
 599                            return await response.read()
 600
 601                        if response.content_type == _APP_JSON:
 602                            json_data = await response.json()
 603
 604                            _LOG.debug(
 605                                "%s %s %s Time %.4fms",
 606                                method,
 607                                f"{endpoint}/{route}",
 608                                f"{response.status} {response.reason}",
 609                                response_time,
 610                            )
 611
 612                            if _LOG.isEnabledFor(TRACE):
 613                                headers.update(response.headers)  # type: ignore
 614
 615                                _LOG.log(
 616                                    TRACE,
 617                                    "%s",
 618                                    error.stringify_http_message(headers),
 619                                )
 620
 621                            # Return the response.
 622                            # oauth2 responses are not packed inside a Response object.
 623                            if oauth2:
 624                                return json_data  # type: ignore[no-any-return]
 625
 626                            return json_data["Response"]  # type: ignore[no-any-return]
 627
 628                    if (
 629                        response.status in _RETRY_5XX
 630                        and retries < self._max_retries  # noqa: W503
 631                    ):
 632                        backoff_ = backoff.ExponentialBackOff(maximum=6)
 633                        sleep_time = next(backoff_)
 634                        _LOG.warning(
 635                            "Got %i - %s. Sleeping for %.2f seconds. Remaining retries: %i",
 636                            response.status,
 637                            response.reason,
 638                            sleep_time,
 639                            self._max_retries - retries,
 640                        )
 641
 642                        retries += 1
 643                        await asyncio.sleep(sleep_time)
 644                        continue
 645
 646                    raise await error.raise_error(response)
 647            # eol
 648            except _Dyn:
 649                continue
 650
 651    if not typing.TYPE_CHECKING:
 652
 653        def __enter__(self) -> typing.NoReturn:
 654            cls = type(self)
 655            raise TypeError(
 656                f"{cls.__qualname__} is async only, use 'async with' instead."
 657            )
 658
 659        def __exit__(
 660            self,
 661            exception_type: typing.Optional[type[BaseException]],
 662            exception: typing.Optional[BaseException],
 663            exception_traceback: typing.Optional[types.TracebackType],
 664        ) -> None:
 665            ...
 666
 667    async def __aenter__(self) -> RESTClient:
 668        self.open()
 669        return self
 670
 671    async def __aexit__(
 672        self,
 673        exception_type: typing.Optional[type[BaseException]],
 674        exception: typing.Optional[BaseException],
 675        exception_traceback: typing.Optional[types.TracebackType],
 676    ) -> None:
 677        await self.close()
 678
 679    # We don't want this to be super complicated.
 680    @staticmethod
 681    @typing.final
 682    async def _handle_ratelimit(
 683        response: aiohttp.ClientResponse,
 684        method: str,
 685        route: str,
 686        max_ratelimit_retries: int = 3,
 687    ) -> None:
 688
 689        if response.status != http.HTTPStatus.TOO_MANY_REQUESTS:
 690            return
 691
 692        if response.content_type != _APP_JSON:
 693            raise error.HTTPError(
 694                f"Being ratelimited on non JSON request, {response.content_type}.",
 695                http.HTTPStatus.TOO_MANY_REQUESTS,
 696            )
 697
 698        count: int = 0
 699        json: typedefs.JSONObject = await response.json()
 700        retry_after = float(json["ThrottleSeconds"])
 701
 702        while True:
 703            if count == max_ratelimit_retries:
 704                raise _Dyn
 705
 706            if retry_after <= 0:
 707                # We sleep for a little bit to avoid funky behavior.
 708                sleep_time = float(random.random() + 0.93) / 2
 709
 710                _LOG.warning(
 711                    "We're being ratelimited with method %s route %s. Sleeping for %.2fs.",
 712                    method,
 713                    route,
 714                    sleep_time,
 715                )
 716                count += 1
 717                await asyncio.sleep(sleep_time)
 718                continue
 719
 720            raise error.RateLimitedError(
 721                body=json,
 722                url=str(response.real_url),
 723                retry_after=retry_after,
 724            )
 725
 726    async def fetch_oauth2_tokens(self, code: str, /) -> builders.OAuth2Response:
 727
 728        if not isinstance(self._client_id, int):
 729            raise TypeError(
 730                f"Expected (int) for client id but got {type(self._client_id).__qualname__}"  # type: ignore
 731            )
 732
 733        if not isinstance(self._client_secret, str):
 734            raise TypeError(
 735                f"Expected (str) for client secret but got {type(self._client_secret).__qualname__}"  # type: ignore
 736            )
 737
 738        headers = {
 739            "client_secret": self._client_secret,
 740        }
 741
 742        data = (
 743            f"grant_type=authorization_code&code={code}"
 744            f"&client_id={self._client_id}&client_secret={self._client_secret}"
 745        )
 746
 747        response = await self._request(
 748            RequestMethod.POST, "", headers=headers, data=data, oauth2=True
 749        )
 750        assert isinstance(response, dict)
 751        return builders.OAuth2Response.build_response(response)
 752
 753    async def refresh_access_token(
 754        self, refresh_token: str, /
 755    ) -> builders.OAuth2Response:
 756        if not isinstance(self._client_id, int):
 757            raise TypeError(
 758                f"Expected (int) for client id but got {type(self._client_id).__qualname__}"  # type: ignore
 759            )
 760
 761        if not isinstance(self._client_secret, str):
 762            raise TypeError(
 763                f"Expected (str) for client secret but got {type(self._client_secret).__qualname__}"  # type: ignore
 764            )
 765
 766        data = {
 767            "grant_type": "refresh_token",
 768            "refresh_token": refresh_token,
 769            "client_id": self._client_id,
 770            "client_secret": self._client_secret,
 771            "Content-Type": "application/x-www-form-urlencoded",
 772        }
 773
 774        response = await self._request(RequestMethod.POST, "", data=data, oauth2=True)
 775        assert isinstance(response, dict)
 776        return builders.OAuth2Response.build_response(response)
 777
 778    async def fetch_bungie_user(self, id: int) -> typedefs.JSONObject:
 779        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 780        resp = await self._request(
 781            RequestMethod.GET, f"User/GetBungieNetUserById/{id}/"
 782        )
 783        assert isinstance(resp, dict)
 784        return resp
 785
 786    async def fetch_user_themes(self) -> typedefs.JSONArray:
 787        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 788        resp = await self._request(RequestMethod.GET, "User/GetAvailableThemes/")
 789        assert isinstance(resp, list)
 790        return resp
 791
 792    async def fetch_membership_from_id(
 793        self,
 794        id: int,
 795        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.NONE,
 796        /,
 797    ) -> typedefs.JSONObject:
 798        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 799        resp = await self._request(
 800            RequestMethod.GET, f"User/GetMembershipsById/{id}/{int(type)}"
 801        )
 802        assert isinstance(resp, dict)
 803        return resp
 804
 805    async def fetch_player(
 806        self,
 807        name: str,
 808        code: int,
 809        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.ALL,
 810        /,
 811    ) -> typedefs.JSONArray:
 812        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 813        resp = await self._request(
 814            RequestMethod.POST,
 815            f"Destiny2/SearchDestinyPlayerByBungieName/{int(type)}",
 816            json={"displayName": name, "displayNameCode": code},
 817        )
 818        assert isinstance(resp, list)
 819        return resp
 820
 821    async def search_users(self, name: str, /) -> typedefs.JSONObject:
 822        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 823        resp = await self._request(
 824            RequestMethod.POST,
 825            "User/Search/GlobalName/0",
 826            json={"displayNamePrefix": name},
 827        )
 828        assert isinstance(resp, dict)
 829        return resp
 830
 831    async def fetch_clan_from_id(
 832        self, id: int, /, access_token: typing.Optional[str] = None
 833    ) -> typedefs.JSONObject:
 834        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 835        resp = await self._request(
 836            RequestMethod.GET, f"GroupV2/{id}", auth=access_token
 837        )
 838        assert isinstance(resp, dict)
 839        return resp
 840
 841    async def fetch_clan(
 842        self,
 843        name: str,
 844        /,
 845        access_token: typing.Optional[str] = None,
 846        *,
 847        type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
 848    ) -> typedefs.JSONObject:
 849        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 850        resp = await self._request(
 851            RequestMethod.GET, f"GroupV2/Name/{name}/{int(type)}", auth=access_token
 852        )
 853        assert isinstance(resp, dict)
 854        return resp
 855
 856    async def fetch_clan_admins(self, clan_id: int, /) -> typedefs.JSONObject:
 857        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 858        resp = await self._request(
 859            RequestMethod.GET, f"GroupV2/{clan_id}/AdminsAndFounder/"
 860        )
 861        assert isinstance(resp, dict)
 862        return resp
 863
 864    async def fetch_clan_conversations(self, clan_id: int, /) -> typedefs.JSONArray:
 865        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 866        resp = await self._request(
 867            RequestMethod.GET, f"GroupV2/{clan_id}/OptionalConversations/"
 868        )
 869        assert isinstance(resp, list)
 870        return resp
 871
 872    async def fetch_application(self, appid: int, /) -> typedefs.JSONObject:
 873        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 874        resp = await self._request(RequestMethod.GET, f"App/Application/{appid}")
 875        assert isinstance(resp, dict)
 876        return resp
 877
 878    async def fetch_character(
 879        self,
 880        member_id: int,
 881        membership_type: typedefs.IntAnd[enums.MembershipType],
 882        character_id: int,
 883        components: list[enums.ComponentType],
 884        auth: typing.Optional[str] = None,
 885    ) -> typedefs.JSONObject:
 886        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 887        collector = _collect_components(components)
 888        response = await self._request(
 889            RequestMethod.GET,
 890            f"Destiny2/{int(membership_type)}/Profile/{member_id}/"
 891            f"Character/{character_id}/?components={collector}",
 892            auth=auth,
 893        )
 894        assert isinstance(response, dict)
 895        return response
 896
 897    async def fetch_activities(
 898        self,
 899        member_id: int,
 900        character_id: int,
 901        mode: typedefs.IntAnd[enums.GameMode],
 902        membership_type: typedefs.IntAnd[
 903            enums.MembershipType
 904        ] = enums.MembershipType.ALL,
 905        *,
 906        page: int = 0,
 907        limit: int = 1,
 908    ) -> typedefs.JSONObject:
 909        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 910        resp = await self._request(
 911            RequestMethod.GET,
 912            f"Destiny2/{int(membership_type)}/Account/"
 913            f"{member_id}/Character/{character_id}/Stats/Activities"
 914            f"/?mode={int(mode)}&count={limit}&page={page}",
 915        )
 916        assert isinstance(resp, dict)
 917        return resp
 918
 919    async def fetch_vendor_sales(self) -> typedefs.JSONObject:
 920        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 921        resp = await self._request(
 922            RequestMethod.GET,
 923            f"Destiny2/Vendors/?components={int(enums.ComponentType.VENDOR_SALES)}",
 924        )
 925        assert isinstance(resp, dict)
 926        return resp
 927
 928    async def fetch_profile(
 929        self,
 930        membership_id: int,
 931        type: typedefs.IntAnd[enums.MembershipType],
 932        components: list[enums.ComponentType],
 933        auth: typing.Optional[str] = None,
 934    ) -> typedefs.JSONObject:
 935        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 936        collector = _collect_components(components)
 937        response = await self._request(
 938            RequestMethod.GET,
 939            f"Destiny2/{int(type)}/Profile/{membership_id}/?components={collector}",
 940            auth=auth,
 941        )
 942        assert isinstance(response, dict)
 943        return response
 944
 945    async def fetch_entity(self, type: str, hash: int) -> typedefs.JSONObject:
 946        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 947        response = await self._request(
 948            RequestMethod.GET, route=f"Destiny2/Manifest/{type}/{hash}"
 949        )
 950        assert isinstance(response, dict)
 951        return response
 952
 953    async def fetch_inventory_item(self, hash: int, /) -> typedefs.JSONObject:
 954        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 955        resp = await self.fetch_entity("DestinyInventoryItemDefinition", hash)
 956        assert isinstance(resp, dict)
 957        return resp
 958
 959    async def fetch_objective_entity(self, hash: int, /) -> typedefs.JSONObject:
 960        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 961        resp = await self.fetch_entity("DestinyObjectiveDefinition", hash)
 962        assert isinstance(resp, dict)
 963        return resp
 964
 965    async def fetch_groups_for_member(
 966        self,
 967        member_id: int,
 968        member_type: typedefs.IntAnd[enums.MembershipType],
 969        /,
 970        *,
 971        filter: int = 0,
 972        group_type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
 973    ) -> typedefs.JSONObject:
 974        resp = await self._request(
 975            RequestMethod.GET,
 976            f"GroupV2/User/{int(member_type)}/{member_id}/{filter}/{int(group_type)}/",
 977        )
 978        assert isinstance(resp, dict)
 979        return resp
 980
 981    async def fetch_potential_groups_for_member(
 982        self,
 983        member_id: int,
 984        member_type: typedefs.IntAnd[enums.MembershipType],
 985        /,
 986        *,
 987        filter: int = 0,
 988        group_type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
 989    ) -> typedefs.JSONObject:
 990        resp = await self._request(
 991            RequestMethod.GET,
 992            f"GroupV2/User/Potential/{int(member_type)}/{member_id}/{filter}/{int(group_type)}/",
 993        )
 994        assert isinstance(resp, dict)
 995        return resp
 996
 997    async def fetch_clan_members(
 998        self,
 999        clan_id: int,
1000        /,
1001        *,
1002        name: typing.Optional[str] = None,
1003        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.NONE,
1004    ) -> typedefs.JSONObject:
1005        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1006        resp = await self._request(
1007            RequestMethod.GET,
1008            f"/GroupV2/{clan_id}/Members/?memberType={int(type)}&nameSearch={name if name else ''}&currentpage=1",
1009        )
1010        assert isinstance(resp, dict)
1011        return resp
1012
1013    async def fetch_hardlinked_credentials(
1014        self,
1015        credential: int,
1016        type: typedefs.IntAnd[enums.CredentialType] = enums.CredentialType.STEAMID,
1017        /,
1018    ) -> typedefs.JSONObject:
1019        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1020        resp = await self._request(
1021            RequestMethod.GET,
1022            f"User/GetMembershipFromHardLinkedCredential/{int(type)}/{credential}/",
1023        )
1024        assert isinstance(resp, dict)
1025        return resp
1026
1027    async def fetch_user_credentials(
1028        self, access_token: str, membership_id: int, /
1029    ) -> typedefs.JSONArray:
1030        resp = await self._request(
1031            RequestMethod.GET,
1032            f"User/GetCredentialTypesForTargetAccount/{membership_id}",
1033            auth=access_token,
1034        )
1035        assert isinstance(resp, list)
1036        return resp
1037
1038    async def insert_socket_plug(
1039        self,
1040        action_token: str,
1041        /,
1042        instance_id: int,
1043        plug: typing.Union[builders.PlugSocketBuilder, dict[str, int]],
1044        character_id: int,
1045        membership_type: typedefs.IntAnd[enums.MembershipType],
1046    ) -> typedefs.JSONObject:
1047
1048        if isinstance(plug, builders.PlugSocketBuilder):
1049            plug = plug.collect()
1050
1051        body = {
1052            "actionToken": action_token,
1053            "itemInstanceId": instance_id,
1054            "plug": plug,
1055            "characterId": character_id,
1056            "membershipType": int(membership_type),
1057        }
1058        resp = await self._request(
1059            RequestMethod.POST, "Destiny2/Actions/Items/InsertSocketPlug", json=body
1060        )
1061        assert isinstance(resp, dict)
1062        return resp
1063
1064    async def insert_socket_plug_free(
1065        self,
1066        access_token: str,
1067        /,
1068        instance_id: int,
1069        plug: typing.Union[builders.PlugSocketBuilder, dict[str, int]],
1070        character_id: int,
1071        membership_type: typedefs.IntAnd[enums.MembershipType],
1072    ) -> typedefs.JSONObject:
1073
1074        if isinstance(plug, builders.PlugSocketBuilder):
1075            plug = plug.collect()
1076
1077        body = {
1078            "itemInstanceId": instance_id,
1079            "plug": plug,
1080            "characterId": character_id,
1081            "membershipType": int(membership_type),
1082        }
1083        resp = await self._request(
1084            RequestMethod.POST,
1085            "Destiny2/Actions/Items/InsertSocketPlugFree",
1086            json=body,
1087            auth=access_token,
1088        )
1089        assert isinstance(resp, dict)
1090        return resp
1091
1092    async def set_item_lock_state(
1093        self,
1094        access_token: str,
1095        state: bool,
1096        /,
1097        item_id: int,
1098        character_id: int,
1099        membership_type: typedefs.IntAnd[enums.MembershipType],
1100    ) -> int:
1101        body = {
1102            "state": state,
1103            "itemId": item_id,
1104            "characterId": character_id,
1105            "membership_type": int(membership_type),
1106        }
1107        response = await self._request(
1108            RequestMethod.POST,
1109            "Destiny2/Actions/Items/SetLockState",
1110            json=body,
1111            auth=access_token,
1112        )
1113        assert isinstance(response, int)
1114        return response
1115
1116    async def set_quest_track_state(
1117        self,
1118        access_token: str,
1119        state: bool,
1120        /,
1121        item_id: int,
1122        character_id: int,
1123        membership_type: typedefs.IntAnd[enums.MembershipType],
1124    ) -> int:
1125        body = {
1126            "state": state,
1127            "itemId": item_id,
1128            "characterId": character_id,
1129            "membership_type": int(membership_type),
1130        }
1131        response = await self._request(
1132            RequestMethod.POST,
1133            "Destiny2/Actions/Items/SetTrackedState",
1134            json=body,
1135            auth=access_token,
1136        )
1137        assert isinstance(response, int)
1138        return response
1139
1140    async def fetch_manifest_path(self) -> typedefs.JSONObject:
1141        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1142        path = await self._request(RequestMethod.GET, "Destiny2/Manifest")
1143        assert isinstance(path, dict)
1144        return path
1145
1146    async def read_manifest_bytes(self, language: str = "en", /) -> bytes:
1147        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1148        _ensure_manifest_language(language)
1149
1150        content = await self.fetch_manifest_path()
1151        resp = await self._request(
1152            RequestMethod.GET,
1153            content["mobileWorldContentPaths"][language],
1154            unwrapping="read",
1155            base=True,
1156        )
1157        assert isinstance(resp, bytes)
1158        return resp
1159
1160    async def download_manifest(
1161        self,
1162        language: str = "en",
1163        name: str = "manifest",
1164        path: typing.Union[pathlib.Path, str] = ".",
1165        *,
1166        force: bool = False,
1167    ) -> None:
1168        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1169        complete_path = _get_path(name, path, sql=True)
1170
1171        if complete_path.exists() and force:
1172            if force:
1173                _LOG.info(
1174                    f"Found manifest in {complete_path!s}. Forcing to Re-Download."
1175                )
1176                complete_path.unlink(missing_ok=True)
1177
1178                return await self.download_manifest(language, name, path, force=force)
1179
1180            else:
1181                raise FileExistsError(
1182                    "Manifest file already exists, "
1183                    "To force download, set the `force` parameter to `True`."
1184                )
1185
1186        _LOG.info(f"Downloading manifest. Location: {complete_path!s}")
1187        data_bytes = await self.read_manifest_bytes(language)
1188        await asyncio.get_running_loop().run_in_executor(
1189            None, _write_sqlite_bytes, data_bytes, path, name
1190        )
1191
1192    async def download_json_manifest(
1193        self,
1194        file_name: str = "manifest",
1195        path: typing.Union[str, pathlib.Path] = ".",
1196        language: str = "en",
1197    ) -> None:
1198        _ensure_manifest_language(language)
1199
1200        _LOG.info(f"Downloading manifest JSON to {_get_path(file_name, path)!r}...")
1201
1202        content = await self.fetch_manifest_path()
1203        json_bytes = await self._request(
1204            RequestMethod.GET,
1205            content["jsonWorldContentPaths"][language],
1206            unwrapping="read",
1207            base=True,
1208        )
1209
1210        await asyncio.get_running_loop().run_in_executor(
1211            None, _write_json_bytes, json_bytes, file_name, path
1212        )
1213        _LOG.info("Finished downloading manifest JSON.")
1214
1215    async def fetch_manifest_version(self) -> str:
1216        return typing.cast(str, (await self.fetch_manifest_path())["version"])
1217
1218    async def fetch_linked_profiles(
1219        self,
1220        member_id: int,
1221        member_type: typedefs.IntAnd[enums.MembershipType],
1222        /,
1223        *,
1224        all: bool = False,
1225    ) -> typedefs.JSONObject:
1226        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1227        resp = await self._request(
1228            RequestMethod.GET,
1229            f"Destiny2/{int(member_type)}/Profile/{member_id}/LinkedProfiles/?getAllMemberships={all}",
1230        )
1231        assert isinstance(resp, dict)
1232        return resp
1233
1234    async def fetch_clan_banners(self) -> typedefs.JSONObject:
1235        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1236        resp = await self._request(
1237            RequestMethod.GET, "Destiny2/Clan/ClanBannerDictionary/"
1238        )
1239        assert isinstance(resp, dict)
1240        return resp
1241
1242    async def fetch_public_milestones(self) -> typedefs.JSONObject:
1243        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1244        resp = await self._request(RequestMethod.GET, "Destiny2/Milestones/")
1245        assert isinstance(resp, dict)
1246        return resp
1247
1248    async def fetch_public_milestone_content(
1249        self, milestone_hash: int, /
1250    ) -> typedefs.JSONObject:
1251        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1252        resp = await self._request(
1253            RequestMethod.GET, f"Destiny2/Milestones/{milestone_hash}/Content/"
1254        )
1255        assert isinstance(resp, dict)
1256        return resp
1257
1258    async def fetch_current_user_memberships(
1259        self, access_token: str, /
1260    ) -> typedefs.JSONObject:
1261        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1262        resp = await self._request(
1263            RequestMethod.GET,
1264            "User/GetMembershipsForCurrentUser/",
1265            auth=access_token,
1266        )
1267        assert isinstance(resp, dict)
1268        return resp
1269
1270    async def equip_item(
1271        self,
1272        access_token: str,
1273        /,
1274        item_id: int,
1275        character_id: int,
1276        membership_type: typedefs.IntAnd[enums.MembershipType],
1277    ) -> None:
1278        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1279        payload = {
1280            "itemId": item_id,
1281            "characterId": character_id,
1282            "membershipType": int(membership_type),
1283        }
1284
1285        await self._request(
1286            RequestMethod.POST,
1287            "Destiny2/Actions/Items/EquipItem/",
1288            json=payload,
1289            auth=access_token,
1290        )
1291
1292    async def equip_items(
1293        self,
1294        access_token: str,
1295        /,
1296        item_ids: list[int],
1297        character_id: int,
1298        membership_type: typedefs.IntAnd[enums.MembershipType],
1299    ) -> None:
1300        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1301        payload = {
1302            "itemIds": item_ids,
1303            "characterId": character_id,
1304            "membershipType": int(membership_type),
1305        }
1306        await self._request(
1307            RequestMethod.POST,
1308            "Destiny2/Actions/Items/EquipItems/",
1309            json=payload,
1310            auth=access_token,
1311        )
1312
1313    async def ban_clan_member(
1314        self,
1315        access_token: str,
1316        /,
1317        group_id: int,
1318        membership_id: int,
1319        membership_type: typedefs.IntAnd[enums.MembershipType],
1320        *,
1321        length: int = 0,
1322        comment: undefined.UndefinedOr[str] = undefined.UNDEFINED,
1323    ) -> None:
1324        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1325        payload = {"comment": str(comment), "length": length}
1326        await self._request(
1327            RequestMethod.POST,
1328            f"GroupV2/{group_id}/Members/{int(membership_type)}/{membership_id}/Ban/",
1329            json=payload,
1330            auth=access_token,
1331        )
1332
1333    async def unban_clan_member(
1334        self,
1335        access_token: str,
1336        /,
1337        group_id: int,
1338        membership_id: int,
1339        membership_type: typedefs.IntAnd[enums.MembershipType],
1340    ) -> None:
1341        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1342        await self._request(
1343            RequestMethod.POST,
1344            f"GroupV2/{group_id}/Members/{int(membership_type)}/{membership_id}/Unban/",
1345            auth=access_token,
1346        )
1347
1348    async def kick_clan_member(
1349        self,
1350        access_token: str,
1351        /,
1352        group_id: int,
1353        membership_id: int,
1354        membership_type: typedefs.IntAnd[enums.MembershipType],
1355    ) -> typedefs.JSONObject:
1356        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1357        resp = await self._request(
1358            RequestMethod.POST,
1359            f"GroupV2/{group_id}/Members/{int(membership_type)}/{membership_id}/Kick/",
1360            auth=access_token,
1361        )
1362        assert isinstance(resp, dict)
1363        return resp
1364
1365    async def edit_clan(
1366        self,
1367        access_token: str,
1368        /,
1369        group_id: int,
1370        *,
1371        name: typedefs.NoneOr[str] = None,
1372        about: typedefs.NoneOr[str] = None,
1373        motto: typedefs.NoneOr[str] = None,
1374        theme: typedefs.NoneOr[str] = None,
1375        tags: typedefs.NoneOr[collections.Sequence[str]] = None,
1376        is_public: typedefs.NoneOr[bool] = None,
1377        locale: typedefs.NoneOr[str] = None,
1378        avatar_image_index: typedefs.NoneOr[int] = None,
1379        membership_option: typedefs.NoneOr[
1380            typedefs.IntAnd[enums.MembershipOption]
1381        ] = None,
1382        allow_chat: typedefs.NoneOr[bool] = None,
1383        chat_security: typedefs.NoneOr[typing.Literal[0, 1]] = None,
1384        call_sign: typedefs.NoneOr[str] = None,
1385        homepage: typedefs.NoneOr[typing.Literal[0, 1, 2]] = None,
1386        enable_invite_messaging_for_admins: typedefs.NoneOr[bool] = None,
1387        default_publicity: typedefs.NoneOr[typing.Literal[0, 1, 2]] = None,
1388        is_public_topic_admin: typedefs.NoneOr[bool] = None,
1389    ) -> None:
1390        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1391        payload = {
1392            "name": name,
1393            "about": about,
1394            "motto": motto,
1395            "theme": theme,
1396            "tags": tags,
1397            "isPublic": is_public,
1398            "avatarImageIndex": avatar_image_index,
1399            "isPublicTopicAdminOnly": is_public_topic_admin,
1400            "allowChat": allow_chat,
1401            "chatSecurity": chat_security,
1402            "callsign": call_sign,
1403            "homepage": homepage,
1404            "enableInvitationMessagingForAdmins": enable_invite_messaging_for_admins,
1405            "defaultPublicity": default_publicity,
1406            "locale": locale,
1407        }
1408        if membership_option is not None:
1409            payload["membershipOption"] = int(membership_option)
1410
1411        await self._request(
1412            RequestMethod.POST,
1413            f"GroupV2/{group_id}/Edit",
1414            json=payload,
1415            auth=access_token,
1416        )
1417
1418    async def edit_clan_options(
1419        self,
1420        access_token: str,
1421        /,
1422        group_id: int,
1423        *,
1424        invite_permissions_override: typedefs.NoneOr[bool] = None,
1425        update_culture_permissionOverride: typedefs.NoneOr[bool] = None,
1426        host_guided_game_permission_override: typedefs.NoneOr[
1427            typing.Literal[0, 1, 2]
1428        ] = None,
1429        update_banner_permission_override: typedefs.NoneOr[bool] = None,
1430        join_level: typedefs.NoneOr[typedefs.IntAnd[enums.ClanMemberType]] = None,
1431    ) -> None:
1432
1433        payload = {
1434            "InvitePermissionOverride": invite_permissions_override,
1435            "UpdateCulturePermissionOverride": update_culture_permissionOverride,
1436            "HostGuidedGamePermissionOverride": host_guided_game_permission_override,
1437            "UpdateBannerPermissionOverride": update_banner_permission_override,
1438            "JoinLevel": int(join_level) if join_level else None,
1439        }
1440
1441        await self._request(
1442            RequestMethod.POST,
1443            f"GroupV2/{group_id}/EditFounderOptions",
1444            json=payload,
1445            auth=access_token,
1446        )
1447
1448    async def fetch_friends(self, access_token: str, /) -> typedefs.JSONObject:
1449        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1450        resp = await self._request(
1451            RequestMethod.GET,
1452            "Social/Friends/",
1453            auth=access_token,
1454        )
1455        assert isinstance(resp, dict)
1456        return resp
1457
1458    async def fetch_friend_requests(self, access_token: str, /) -> typedefs.JSONObject:
1459        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1460        resp = await self._request(
1461            RequestMethod.GET,
1462            "Social/Friends/Requests",
1463            auth=access_token,
1464        )
1465        assert isinstance(resp, dict)
1466        return resp
1467
1468    async def accept_friend_request(self, access_token: str, /, member_id: int) -> None:
1469        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1470        await self._request(
1471            RequestMethod.POST,
1472            f"Social/Friends/Requests/Accept/{member_id}",
1473            auth=access_token,
1474        )
1475
1476    async def send_friend_request(self, access_token: str, /, member_id: int) -> None:
1477        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1478        await self._request(
1479            RequestMethod.POST,
1480            f"Social/Friends/Add/{member_id}",
1481            auth=access_token,
1482        )
1483
1484    async def decline_friend_request(
1485        self, access_token: str, /, member_id: int
1486    ) -> None:
1487        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1488        await self._request(
1489            RequestMethod.POST,
1490            f"Social/Friends/Requests/Decline/{member_id}",
1491            auth=access_token,
1492        )
1493
1494    async def remove_friend(self, access_token: str, /, member_id: int) -> None:
1495        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1496        await self._request(
1497            RequestMethod.POST,
1498            f"Social/Friends/Remove/{member_id}",
1499            auth=access_token,
1500        )
1501
1502    async def remove_friend_request(self, access_token: str, /, member_id: int) -> None:
1503        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1504        await self._request(
1505            RequestMethod.POST,
1506            f"Social/Friends/Requests/Remove/{member_id}",
1507            auth=access_token,
1508        )
1509
1510    async def approve_all_pending_group_users(
1511        self,
1512        access_token: str,
1513        /,
1514        group_id: int,
1515        message: undefined.UndefinedOr[str] = undefined.UNDEFINED,
1516    ) -> None:
1517        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1518        await self._request(
1519            RequestMethod.POST,
1520            f"GroupV2/{group_id}/Members/ApproveAll",
1521            auth=access_token,
1522            json={"message": str(message)},
1523        )
1524
1525    async def deny_all_pending_group_users(
1526        self,
1527        access_token: str,
1528        /,
1529        group_id: int,
1530        *,
1531        message: undefined.UndefinedOr[str] = undefined.UNDEFINED,
1532    ) -> None:
1533        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1534        await self._request(
1535            RequestMethod.POST,
1536            f"GroupV2/{group_id}/Members/DenyAll",
1537            auth=access_token,
1538            json={"message": str(message)},
1539        )
1540
1541    async def add_optional_conversation(
1542        self,
1543        access_token: str,
1544        /,
1545        group_id: int,
1546        *,
1547        name: undefined.UndefinedOr[str] = undefined.UNDEFINED,
1548        security: typing.Literal[0, 1] = 0,
1549    ) -> None:
1550        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1551        payload = {"chatName": str(name), "chatSecurity": security}
1552        await self._request(
1553            RequestMethod.POST,
1554            f"GroupV2/{group_id}/OptionalConversations/Add",
1555            json=payload,
1556            auth=access_token,
1557        )
1558
1559    async def edit_optional_conversation(
1560        self,
1561        access_token: str,
1562        /,
1563        group_id: int,
1564        conversation_id: int,
1565        *,
1566        name: undefined.UndefinedOr[str] = undefined.UNDEFINED,
1567        security: typing.Literal[0, 1] = 0,
1568        enable_chat: bool = False,
1569    ) -> None:
1570        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1571        payload = {
1572            "chatEnabled": enable_chat,
1573            "chatName": str(name),
1574            "chatSecurity": security,
1575        }
1576        await self._request(
1577            RequestMethod.POST,
1578            f"GroupV2/{group_id}/OptionalConversations/Edit/{conversation_id}",
1579            json=payload,
1580            auth=access_token,
1581        )
1582
1583    async def transfer_item(
1584        self,
1585        access_token: str,
1586        /,
1587        item_id: int,
1588        item_hash: int,
1589        character_id: int,
1590        member_type: typedefs.IntAnd[enums.MembershipType],
1591        *,
1592        stack_size: int = 1,
1593        vault: bool = False,
1594    ) -> None:
1595        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1596        payload = {
1597            "characterId": character_id,
1598            "membershipType": int(member_type),
1599            "itemId": item_id,
1600            "itemReferenceHash": item_hash,
1601            "stackSize": stack_size,
1602            "transferToVault": vault,
1603        }
1604        await self._request(
1605            RequestMethod.POST,
1606            "Destiny2/Actions/Items/TransferItem",
1607            json=payload,
1608            auth=access_token,
1609        )
1610
1611    async def pull_item(
1612        self,
1613        access_token: str,
1614        /,
1615        item_id: int,
1616        item_hash: int,
1617        character_id: int,
1618        member_type: typedefs.IntAnd[enums.MembershipType],
1619        *,
1620        stack_size: int = 1,
1621        vault: bool = False,
1622    ) -> None:
1623        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1624        payload = {
1625            "characterId": character_id,
1626            "membershipType": int(member_type),
1627            "itemId": item_id,
1628            "itemReferenceHash": item_hash,
1629            "stackSize": stack_size,
1630            "transferToVault": vault,
1631        }
1632        await self._request(
1633            RequestMethod.POST,
1634            "Destiny2/Actions/Items/PullFromPostmaster",
1635            json=payload,
1636            auth=access_token,
1637        )
1638
1639    async def fetch_fireteams(
1640        self,
1641        activity_type: typedefs.IntAnd[fireteams.FireteamActivity],
1642        *,
1643        platform: typedefs.IntAnd[
1644            fireteams.FireteamPlatform
1645        ] = fireteams.FireteamPlatform.ANY,
1646        language: typing.Union[
1647            fireteams.FireteamLanguage, str
1648        ] = fireteams.FireteamLanguage.ALL,
1649        date_range: typedefs.IntAnd[
1650            fireteams.FireteamDate
1651        ] = fireteams.FireteamDate.ALL,
1652        page: int = 0,
1653        slots_filter: int = 0,
1654    ) -> typedefs.JSONObject:
1655        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1656        resp = await self._request(
1657            RequestMethod.GET,
1658            f"Fireteam/Search/Available/{int(platform)}/{int(activity_type)}/{int(date_range)}/{slots_filter}/{page}/?langFilter={str(language)}",  # noqa: E501 Line too long
1659        )
1660        assert isinstance(resp, dict)
1661        return resp
1662
1663    async def fetch_avaliable_clan_fireteams(
1664        self,
1665        access_token: str,
1666        group_id: int,
1667        activity_type: typedefs.IntAnd[fireteams.FireteamActivity],
1668        *,
1669        platform: typedefs.IntAnd[fireteams.FireteamPlatform],
1670        language: typing.Union[fireteams.FireteamLanguage, str],
1671        date_range: typedefs.IntAnd[
1672            fireteams.FireteamDate
1673        ] = fireteams.FireteamDate.ALL,
1674        page: int = 0,
1675        public_only: bool = False,
1676        slots_filter: int = 0,
1677    ) -> typedefs.JSONObject:
1678        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1679        resp = await self._request(
1680            RequestMethod.GET,
1681            f"Fireteam/Clan/{group_id}/Available/{int(platform)}/{int(activity_type)}/{int(date_range)}/{slots_filter}/{public_only}/{page}",  # noqa: E501
1682            json={"langFilter": str(language)},
1683            auth=access_token,
1684        )
1685        assert isinstance(resp, dict)
1686        return resp
1687
1688    async def fetch_clan_fireteam(
1689        self, access_token: str, fireteam_id: int, group_id: int
1690    ) -> typedefs.JSONObject:
1691        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1692        resp = await self._request(
1693            RequestMethod.GET,
1694            f"Fireteam/Clan/{group_id}/Summary/{fireteam_id}",
1695            auth=access_token,
1696        )
1697        assert isinstance(resp, dict)
1698        return resp
1699
1700    async def fetch_my_clan_fireteams(
1701        self,
1702        access_token: str,
1703        group_id: int,
1704        *,
1705        include_closed: bool = True,
1706        platform: typedefs.IntAnd[fireteams.FireteamPlatform],
1707        language: typing.Union[fireteams.FireteamLanguage, str],
1708        filtered: bool = True,
1709        page: int = 0,
1710    ) -> typedefs.JSONObject:
1711        payload = {"groupFilter": filtered, "langFilter": str(language)}
1712        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1713        resp = await self._request(
1714            RequestMethod.GET,
1715            f"Fireteam/Clan/{group_id}/My/{int(platform)}/{include_closed}/{page}",
1716            json=payload,
1717            auth=access_token,
1718        )
1719        assert isinstance(resp, dict)
1720        return resp
1721
1722    async def fetch_private_clan_fireteams(
1723        self, access_token: str, group_id: int, /
1724    ) -> int:
1725        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1726        resp = await self._request(
1727            RequestMethod.GET,
1728            f"Fireteam/Clan/{group_id}/ActiveCount",
1729            auth=access_token,
1730        )
1731        assert isinstance(resp, int)
1732        return resp
1733
1734    async def fetch_post_activity(self, instance_id: int, /) -> typedefs.JSONObject:
1735        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1736        resp = await self._request(
1737            RequestMethod.GET, f"Destiny2/Stats/PostGameCarnageReport/{instance_id}"
1738        )
1739        assert isinstance(resp, dict)
1740        return resp
1741
1742    async def search_entities(
1743        self, name: str, entity_type: str, *, page: int = 0
1744    ) -> typedefs.JSONObject:
1745        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1746        resp = await self._request(
1747            RequestMethod.GET,
1748            f"Destiny2/Armory/Search/{entity_type}/{name}/",
1749            json={"page": page},
1750        )
1751        assert isinstance(resp, dict)
1752        return resp
1753
1754    async def fetch_unique_weapon_history(
1755        self,
1756        membership_id: int,
1757        character_id: int,
1758        membership_type: typedefs.IntAnd[enums.MembershipType],
1759    ) -> typedefs.JSONObject:
1760        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1761        resp = await self._request(
1762            RequestMethod.GET,
1763            f"Destiny2/{int(membership_type)}/Account/{membership_id}/Character/{character_id}/Stats/UniqueWeapons/",
1764        )
1765        assert isinstance(resp, dict)
1766        return resp
1767
1768    async def fetch_item(
1769        self,
1770        member_id: int,
1771        item_id: int,
1772        membership_type: typedefs.IntAnd[enums.MembershipType],
1773        components: list[enums.ComponentType],
1774    ) -> typedefs.JSONObject:
1775        collector = _collect_components(components)
1776        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1777        resp = await self._request(
1778            RequestMethod.GET,
1779            f"Destiny2/{int(membership_type)}/Profile/{member_id}/Item/{item_id}/?components={collector}",
1780        )
1781        assert isinstance(resp, dict)
1782        return resp
1783
1784    async def fetch_clan_weekly_rewards(self, clan_id: int, /) -> typedefs.JSONObject:
1785        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1786        resp = await self._request(
1787            RequestMethod.GET, f"Destiny2/Clan/{clan_id}/WeeklyRewardState/"
1788        )
1789        assert isinstance(resp, dict)
1790        return resp
1791
1792    async def fetch_available_locales(self) -> typedefs.JSONObject:
1793        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1794        resp = await self._request(
1795            RequestMethod.GET, "Destiny2/Manifest/DestinyLocaleDefinition/"
1796        )
1797        assert isinstance(resp, dict)
1798        return resp
1799
1800    async def fetch_common_settings(self) -> typedefs.JSONObject:
1801        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1802        resp = await self._request(RequestMethod.GET, "Settings")
1803        assert isinstance(resp, dict)
1804        return resp
1805
1806    async def fetch_user_systems_overrides(self) -> typedefs.JSONObject:
1807        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1808        resp = await self._request(RequestMethod.GET, "UserSystemOverrides")
1809        assert isinstance(resp, dict)
1810        return resp
1811
1812    async def fetch_global_alerts(
1813        self, *, include_streaming: bool = False
1814    ) -> typedefs.JSONArray:
1815        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1816        resp = await self._request(
1817            RequestMethod.GET, f"GlobalAlerts/?includestreaming={include_streaming}"
1818        )
1819        assert isinstance(resp, list)
1820        return resp
1821
1822    async def awainitialize_request(
1823        self,
1824        access_token: str,
1825        type: typing.Literal[0, 1],
1826        membership_type: typedefs.IntAnd[enums.MembershipType],
1827        /,
1828        *,
1829        affected_item_id: typing.Optional[int] = None,
1830        character_id: typing.Optional[int] = None,
1831    ) -> typedefs.JSONObject:
1832        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1833
1834        body = {"type": type, "membershipType": int(membership_type)}
1835
1836        if affected_item_id is not None:
1837            body["affectedItemId"] = affected_item_id
1838
1839        if character_id is not None:
1840            body["characterId"] = character_id
1841
1842        resp = await self._request(
1843            RequestMethod.POST, "Destiny2/Awa/Initialize", json=body, auth=access_token
1844        )
1845        assert isinstance(resp, dict)
1846        return resp
1847
1848    async def awaget_action_token(
1849        self, access_token: str, correlation_id: str, /
1850    ) -> typedefs.JSONObject:
1851        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1852        resp = await self._request(
1853            RequestMethod.POST,
1854            f"Destiny2/Awa/GetActionToken/{correlation_id}",
1855            auth=access_token,
1856        )
1857        assert isinstance(resp, dict)
1858        return resp
1859
1860    async def awa_provide_authorization_result(
1861        self,
1862        access_token: str,
1863        selection: int,
1864        correlation_id: str,
1865        nonce: collections.MutableSequence[typing.Union[str, bytes]],
1866    ) -> int:
1867        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1868
1869        body = {"selection": selection, "correlationId": correlation_id, "nonce": nonce}
1870
1871        resp = await self._request(
1872            RequestMethod.POST,
1873            "Destiny2/Awa/AwaProvideAuthorizationResult",
1874            json=body,
1875            auth=access_token,
1876        )
1877        assert isinstance(resp, int)
1878        return resp
1879
1880    async def fetch_vendors(
1881        self,
1882        access_token: str,
1883        character_id: int,
1884        membership_id: int,
1885        membership_type: typedefs.IntAnd[enums.MembershipType],
1886        /,
1887        components: list[enums.ComponentType],
1888        filter: typing.Optional[int] = None,
1889    ) -> typedefs.JSONObject:
1890        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1891        components_ = _collect_components(components)
1892        route = (
1893            f"Destiny2/{int(membership_type)}/Profile/{membership_id}"
1894            f"/Character/{character_id}/Vendors/?components={components_}"
1895        )
1896
1897        if filter is not None:
1898            route = route + f"&filter={filter}"
1899
1900        resp = await self._request(
1901            RequestMethod.GET,
1902            route,
1903            auth=access_token,
1904        )
1905        assert isinstance(resp, dict)
1906        return resp
1907
1908    async def fetch_vendor(
1909        self,
1910        access_token: str,
1911        character_id: int,
1912        membership_id: int,
1913        membership_type: typedefs.IntAnd[enums.MembershipType],
1914        vendor_hash: int,
1915        /,
1916        components: list[enums.ComponentType],
1917    ) -> typedefs.JSONObject:
1918        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1919        components_ = _collect_components(components)
1920        resp = await self._request(
1921            RequestMethod.GET,
1922            (
1923                f"Platform/Destiny2/{int(membership_type)}/Profile/{membership_id}"
1924                f"/Character/{character_id}/Vendors/{vendor_hash}/?components={components_}"
1925            ),
1926            auth=access_token,
1927        )
1928        assert isinstance(resp, dict)
1929        return resp
1930
1931    async def fetch_application_api_usage(
1932        self,
1933        access_token: str,
1934        application_id: int,
1935        /,
1936        *,
1937        start: typing.Optional[datetime.datetime] = None,
1938        end: typing.Optional[datetime.datetime] = None,
1939    ) -> typedefs.JSONObject:
1940
1941        end_date, start_date = time.parse_date_range(end, start)
1942        resp = await self._request(
1943            RequestMethod.GET,
1944            f"App/ApiUsage/{application_id}/?end={end_date}&start={start_date}",
1945            auth=access_token,
1946        )
1947        assert isinstance(resp, dict)
1948        return resp
1949
1950    async def fetch_bungie_applications(self) -> typedefs.JSONArray:
1951        resp = await self._request(RequestMethod.GET, "App/FirstParty")
1952        assert isinstance(resp, list)
1953        return resp
1954
1955    async def fetch_content_type(self, type: str, /) -> typedefs.JSONObject:
1956        resp = await self._request(RequestMethod.GET, f"Content/GetContentType/{type}/")
1957        assert isinstance(resp, dict)
1958        return resp
1959
1960    async def fetch_content_by_id(
1961        self, id: int, locale: str, /, *, head: bool = False
1962    ) -> typedefs.JSONObject:
1963        resp = await self._request(
1964            RequestMethod.GET,
1965            f"Content/GetContentById/{id}/{locale}/",
1966            json={"head": head},
1967        )
1968        assert isinstance(resp, dict)
1969        return resp
1970
1971    async def fetch_content_by_tag_and_type(
1972        self, locale: str, tag: str, type: str, *, head: bool = False
1973    ) -> typedefs.JSONObject:
1974        resp = await self._request(
1975            RequestMethod.GET,
1976            f"Content/GetContentByTagAndType/{tag}/{type}/{locale}/",
1977            json={"head": head},
1978        )
1979        assert isinstance(resp, dict)
1980        return resp
1981
1982    async def search_content_with_text(
1983        self,
1984        locale: str,
1985        /,
1986        content_type: str,
1987        search_text: str,
1988        tag: str,
1989        *,
1990        page: undefined.UndefinedOr[int] = undefined.UNDEFINED,
1991        source: undefined.UndefinedOr[str] = undefined.UNDEFINED,
1992    ) -> typedefs.JSONObject:
1993
1994        body: typedefs.JSONObject = {}
1995
1996        body["ctype"] = content_type
1997        body["searchtext"] = search_text
1998        body["tag"] = tag
1999
2000        if page is not undefined.UNDEFINED:
2001            body["currentpage"] = page
2002        else:
2003            body["currentpage"] = 1
2004
2005        if source is not undefined.UNDEFINED:
2006            body["source"] = source
2007        else:
2008            source = ""
2009        resp = await self._request(
2010            RequestMethod.GET, f"Content/Search/{locale}/", json=body
2011        )
2012        assert isinstance(resp, dict)
2013        return resp
2014
2015    async def search_content_by_tag_and_type(
2016        self,
2017        locale: str,
2018        tag: str,
2019        type: str,
2020        *,
2021        page: undefined.UndefinedOr[int] = undefined.UNDEFINED,
2022    ) -> typedefs.JSONObject:
2023        body: typedefs.JSONObject = {}
2024        body["currentpage"] = 1 if page is undefined.UNDEFINED else page
2025        resp = await self._request(
2026            RequestMethod.GET,
2027            f"Content/SearchContentByTagAndType/{tag}/{type}/{locale}/",
2028            json=body,
2029        )
2030        assert isinstance(resp, dict)
2031        return resp
2032
2033    async def search_help_articles(
2034        self, text: str, size: str, /
2035    ) -> typedefs.JSONObject:
2036        resp = await self._request(
2037            RequestMethod.GET, f"Content/SearchHelpArticles/{text}/{size}/"
2038        )
2039        assert isinstance(resp, dict)
2040        return resp
2041
2042    async def fetch_topics_page(
2043        self,
2044        category_filter: int,
2045        group: int,
2046        date_filter: int,
2047        sort: typing.Union[str, bytes],
2048        *,
2049        page: undefined.UndefinedOr[int] = undefined.UNDEFINED,
2050        locales: undefined.UndefinedOr[collections.Iterable[str]] = undefined.UNDEFINED,
2051        tag_filter: undefined.UndefinedOr[str] = undefined.UNDEFINED,
2052    ) -> typedefs.JSONObject:
2053
2054        body: typedefs.JSONObject = {}
2055        if locales is not undefined.UNDEFINED:
2056            body["locales"] = ",".join(str(locales))
2057        else:
2058            body["locales"] = ",".join([])
2059
2060        if tag_filter is not undefined.UNDEFINED:
2061            body["tagstring"] = tag_filter
2062        else:
2063            body["tagstring"] = ""
2064
2065        page = 0 if page is not undefined.UNDEFINED else page
2066
2067        resp = await self._request(
2068            RequestMethod.GET,
2069            f"Forum/GetTopicsPaged/{page}/{0}/{group}/{sort!s}/{date_filter}/{category_filter}/",
2070            json=body,
2071        )
2072        assert isinstance(resp, dict)
2073        return resp
2074
2075    async def fetch_core_topics_page(
2076        self,
2077        category_filter: int,
2078        date_filter: int,
2079        sort: typing.Union[str, bytes],
2080        *,
2081        page: undefined.UndefinedOr[int] = undefined.UNDEFINED,
2082        locales: undefined.UndefinedOr[collections.Iterable[str]] = undefined.UNDEFINED,
2083    ) -> typedefs.JSONObject:
2084        body: typedefs.JSONObject = {}
2085
2086        if locales is not undefined.UNDEFINED:
2087            body["locales"] = ",".join(str(locales))
2088        else:
2089            body["locales"] = ",".join([])
2090
2091        resp = await self._request(
2092            RequestMethod.GET,
2093            f"Forum/GetCoreTopicsPaged/{0 if page is undefined.UNDEFINED else page}"
2094            f"/{sort!s}/{date_filter}/{category_filter}/",
2095            json=body,
2096        )
2097        assert isinstance(resp, dict)
2098        return resp
2099
2100    async def fetch_posts_threaded_page(
2101        self,
2102        parent_post: bool,
2103        page: int,
2104        page_size: int,
2105        parent_post_id: int,
2106        reply_size: int,
2107        root_thread_mode: bool,
2108        sort_mode: int,
2109        show_banned: typing.Optional[str] = None,
2110    ) -> typedefs.JSONObject:
2111        resp = await self._request(
2112            RequestMethod.GET,
2113            f"Forum/GetPostsThreadedPaged/{parent_post}/{page}/"
2114            f"{page_size}/{reply_size}/{parent_post_id}/{root_thread_mode}/{sort_mode}/",
2115            json={"showbanned": show_banned},
2116        )
2117        assert isinstance(resp, dict)
2118        return resp
2119
2120    async def fetch_posts_threaded_page_from_child(
2121        self,
2122        child_id: bool,
2123        page: int,
2124        page_size: int,
2125        reply_size: int,
2126        root_thread_mode: bool,
2127        sort_mode: int,
2128        show_banned: typing.Optional[str] = None,
2129    ) -> typedefs.JSONObject:
2130        resp = await self._request(
2131            RequestMethod.GET,
2132            f"Forum/GetPostsThreadedPagedFromChild/{child_id}/"
2133            f"{page}/{page_size}/{reply_size}/{root_thread_mode}/{sort_mode}/",
2134            json={"showbanned": show_banned},
2135        )
2136        assert isinstance(resp, dict)
2137        return resp
2138
2139    async def fetch_post_and_parent(
2140        self, child_id: int, /, *, show_banned: typing.Optional[str] = None
2141    ) -> typedefs.JSONObject:
2142        resp = await self._request(
2143            RequestMethod.GET,
2144            f"Forum/GetPostAndParent/{child_id}/",
2145            json={"showbanned": show_banned},
2146        )
2147        assert isinstance(resp, dict)
2148        return resp
2149
2150    async def fetch_posts_and_parent_awaiting(
2151        self, child_id: int, /, *, show_banned: typing.Optional[str] = None
2152    ) -> typedefs.JSONObject:
2153        resp = await self._request(
2154            RequestMethod.GET,
2155            f"Forum/GetPostAndParentAwaitingApproval/{child_id}/",
2156            json={"showbanned": show_banned},
2157        )
2158        assert isinstance(resp, dict)
2159        return resp
2160
2161    async def fetch_topic_for_content(self, content_id: int, /) -> int:
2162        resp = await self._request(
2163            RequestMethod.GET, f"Forum/GetTopicForContent/{content_id}/"
2164        )
2165        assert isinstance(resp, int)
2166        return resp
2167
2168    async def fetch_forum_tag_suggestions(
2169        self, partial_tag: str, /
2170    ) -> typedefs.JSONObject:
2171        resp = await self._request(
2172            RequestMethod.GET,
2173            "Forum/GetForumTagSuggestions/",
2174            json={"partialtag": partial_tag},
2175        )
2176        assert isinstance(resp, dict)
2177        return resp
2178
2179    async def fetch_poll(self, topic_id: int, /) -> typedefs.JSONObject:
2180        resp = await self._request(RequestMethod.GET, f"Forum/Poll/{topic_id}/")
2181        assert isinstance(resp, dict)
2182        return resp
2183
2184    async def fetch_recuirement_thread_summaries(self) -> typedefs.JSONArray:
2185        resp = await self._request(RequestMethod.POST, "Forum/Recruit/Summaries/")
2186        assert isinstance(resp, list)
2187        return resp
2188
2189    async def fetch_recommended_groups(
2190        self,
2191        accecss_token: str,
2192        /,
2193        *,
2194        date_range: int = 0,
2195        group_type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
2196    ) -> typedefs.JSONArray:
2197        resp = await self._request(
2198            RequestMethod.POST,
2199            f"GroupV2/Recommended/{int(group_type)}/{date_range}/",
2200            auth=accecss_token,
2201        )
2202        assert isinstance(resp, list)
2203        return resp
2204
2205    async def fetch_available_avatars(self) -> collections.Mapping[str, int]:
2206        resp = await self._request(RequestMethod.GET, "GroupV2/GetAvailableAvatars/")
2207        assert isinstance(resp, dict)
2208        return resp
2209
2210    async def fetch_user_clan_invite_setting(
2211        self,
2212        access_token: str,
2213        /,
2214        membership_type: typedefs.IntAnd[enums.MembershipType],
2215    ) -> bool:
2216        resp = await self._request(
2217            RequestMethod.GET,
2218            f"GroupV2/GetUserClanInviteSetting/{int(membership_type)}/",
2219            auth=access_token,
2220        )
2221        assert isinstance(resp, bool)
2222        return resp
2223
2224    async def fetch_banned_group_members(
2225        self, access_token: str, group_id: int, /, *, page: int = 1
2226    ) -> typedefs.JSONObject:
2227        resp = await self._request(
2228            RequestMethod.GET,
2229            f"GroupV2/{group_id}/Banned/?currentpage={page}",
2230            auth=access_token,
2231        )
2232        assert isinstance(resp, dict)
2233        return resp
2234
2235    async def fetch_pending_group_memberships(
2236        self, access_token: str, group_id: int, /, *, current_page: int = 1
2237    ) -> typedefs.JSONObject:
2238        resp = await self._request(
2239            RequestMethod.GET,
2240            f"GroupV2/{group_id}/Members/Pending/?currentpage={current_page}",
2241            auth=access_token,
2242        )
2243        assert isinstance(resp, dict)
2244        return resp
2245
2246    async def fetch_invited_group_memberships(
2247        self, access_token: str, group_id: int, /, *, current_page: int = 1
2248    ) -> typedefs.JSONObject:
2249        resp = await self._request(
2250            RequestMethod.GET,
2251            f"GroupV2/{group_id}/Members/InvitedIndividuals/?currentpage={current_page}",
2252            auth=access_token,
2253        )
2254        assert isinstance(resp, dict)
2255        return resp
2256
2257    async def invite_member_to_group(
2258        self,
2259        access_token: str,
2260        /,
2261        group_id: int,
2262        membership_id: int,
2263        membership_type: typedefs.IntAnd[enums.MembershipType],
2264        *,
2265        message: undefined.UndefinedOr[str] = undefined.UNDEFINED,
2266    ) -> typedefs.JSONObject:
2267        resp = await self._request(
2268            RequestMethod.POST,
2269            f"GroupV2/{group_id}/Members/IndividualInvite/{int(membership_type)}/{membership_id}/",
2270            auth=access_token,
2271            json={"message": str(message)},
2272        )
2273        assert isinstance(resp, dict)
2274        return resp
2275
2276    async def cancel_group_member_invite(
2277        self,
2278        access_token: str,
2279        /,
2280        group_id: int,
2281        membership_id: int,
2282        membership_type: typedefs.IntAnd[enums.MembershipType],
2283    ) -> typedefs.JSONObject:
2284        resp = await self._request(
2285            RequestMethod.POST,
2286            f"GroupV2/{group_id}/Members/IndividualInviteCancel/{int(membership_type)}/{membership_id}/",
2287            auth=access_token,
2288        )
2289        assert isinstance(resp, dict)
2290        return resp
2291
2292    async def fetch_historical_definition(self) -> typedefs.JSONObject:
2293        resp = await self._request(RequestMethod.GET, "Destiny2/Stats/Definition/")
2294        assert isinstance(resp, dict)
2295        return resp
2296
2297    async def fetch_historical_stats(
2298        self,
2299        character_id: int,
2300        membership_id: int,
2301        membership_type: typedefs.IntAnd[enums.MembershipType],
2302        day_start: datetime.datetime,
2303        day_end: datetime.datetime,
2304        groups: list[typedefs.IntAnd[enums.StatsGroupType]],
2305        modes: collections.Sequence[typedefs.IntAnd[enums.GameMode]],
2306        *,
2307        period_type: enums.PeriodType = enums.PeriodType.ALL_TIME,
2308    ) -> typedefs.JSONObject:
2309
2310        end, start = time.parse_date_range(day_end, day_start)
2311        resp = await self._request(
2312            RequestMethod.GET,
2313            f"Destiny2/{int(membership_type)}/Account/{membership_id}/Character/{character_id}/Stats/",
2314            json={
2315                "dayend": end,
2316                "daystart": start,
2317                "groups": [str(int(group)) for group in groups],
2318                "modes": [str(int(mode)) for mode in modes],
2319                "periodType": int(period_type),
2320            },
2321        )
2322        assert isinstance(resp, dict)
2323        return resp
2324
2325    async def fetch_historical_stats_for_account(
2326        self,
2327        membership_id: int,
2328        membership_type: typedefs.IntAnd[enums.MembershipType],
2329        groups: list[typedefs.IntAnd[enums.StatsGroupType]],
2330    ) -> typedefs.JSONObject:
2331        resp = await self._request(
2332            RequestMethod.GET,
2333            f"Destiny2/{int(membership_type)}/Account/{membership_id}/Stats/",
2334            json={"groups": [str(int(group)) for group in groups]},
2335        )
2336        assert isinstance(resp, dict)
2337        return resp
2338
2339    async def fetch_aggregated_activity_stats(
2340        self,
2341        character_id: int,
2342        membership_id: int,
2343        membership_type: typedefs.IntAnd[enums.MembershipType],
2344        /,
2345    ) -> typedefs.JSONObject:
2346        resp = await self._request(
2347            RequestMethod.GET,
2348            f"Destiny2/{int(membership_type)}/Account/{membership_id}/"
2349            f"Character/{character_id}/Stats/AggregateActivityStats/",
2350        )
2351        assert isinstance(resp, dict)
2352        return resp

A RESTful client implementation for Bungie's API.

This client is designed to only make HTTP requests and return JSON objects to provide RESTful functionality.

This client is also used within aiobungie.Client which deserialize those returned JSON objects using the factory into Pythonic data classes objects which provide Python functionality.

Example
import aiobungie

async def main():
    async with aiobungie.RESTClient("TOKEN") as rest_client:
        req = await rest_client.fetch_clan_members(4389205)
        clan_members = req['results']
        for member in clan_members:
            for k, v in member['destinyUserInfo'].items():
                print(k, v)
Parameters
  • token (str): A valid application token from Bungie's developer portal.
Other Parameters
  • max_retries (int): The max retries number to retry if the request hit a 5xx status code.
  • max_ratelimit_retries (int): The max retries number to retry if the request hit a 429 status code. Defaults to 3.
  • client_secret (typing.Optional[str]): An optional application client secret, This is only needed if you're fetching OAuth2 tokens with this client.
  • client_id (typing.Optional[int]): An optional application client id, This is only needed if you're fetching OAuth2 tokens with this client.
  • enable_debugging (bool | str): Whether to enable logging responses or not.
Logging Levels
  • False: This will disable logging.
  • True: This will set the level to DEBUG and enable logging minimal information.
  • "TRACE" | aiobungie.TRACE: This will log the response headers along with the minimal information.
RESTClient( token: str, /, client_secret: Optional[str] = None, client_id: Optional[int] = None, *, max_retries: int = 4, max_ratelimit_retries: int = 3, enable_debugging: Union[Literal['TRACE'], bool, int] = False)
423    def __init__(
424        self,
425        token: str,
426        /,
427        client_secret: typing.Optional[str] = None,
428        client_id: typing.Optional[int] = None,
429        *,
430        max_retries: int = 4,
431        max_ratelimit_retries: int = 3,
432        enable_debugging: typing.Union[typing.Literal["TRACE"], bool, int] = False,
433    ) -> None:
434        self._session: typing.Optional[_Session] = None
435        self._lock: typing.Optional[asyncio.Lock] = None
436        self._client_secret = client_secret
437        self._client_id = client_id
438        self._token: str = token
439        self._max_retries = max_retries
440        self._max_rate_limit_retries = max_ratelimit_retries
441        self._metadata: collections.MutableMapping[typing.Any, typing.Any] = {}
442
443        self._set_debug_level(enable_debugging)
client_id: Optional[int]

Return the client id of this REST client if provided, Otherwise None.

metadata: collections.abc.MutableMapping[typing.Any, typing.Any]

A mutable mapping storage for the user's needs.

This mapping is useful for storing any kind of data that the user may need.

Example
import aiobungie

client = aiobungie.RESTClient(…)

async with client:
    # Fetch auth tokens and store them
    client.metadata["tokens"] = await client.fetch_access_token("code")

# Some other time.
async with client:
    # Retrieve the tokens
    tokens: aiobungie.OAuth2Response = client.metadata["tokens"]

    # Use them to fetch your user.
    user = await client.fetch_current_user_memberships(tokens.access_token)
is_alive: bool

Returns True if the REST client is alive and False otherwise.

@typing.final
async def close(self) -> None:
457    @typing.final
458    async def close(self) -> None:
459        session = self._get_session()
460        await session.close()
461        self._session = None

Close this REST client session if it was acquired.

This method is automatically called when using async with contextmanager.

Raises
  • RuntimeError: If the client is already closed.
@typing.final
def open(self) -> None:
463    @typing.final
464    def open(self) -> None:
465        """Open a new client session. This is called internally with contextmanager usage."""
466        if self.is_alive:
467            raise RuntimeError("Cannot open a new session while it's already open.")
468
469        self._session = _Session.create(owner=False, raise_status=False)

Open a new client session. This is called internally with contextmanager usage.

@typing.final
def enable_debugging( self, level: Union[Literal['TRACE'], bool, int] = False, file: Union[pathlib.Path, str, NoneType] = None, /) -> None:
471    @typing.final
472    def enable_debugging(
473        self,
474        level: typing.Union[typing.Literal["TRACE"], bool, int] = False,
475        file: typing.Optional[typing.Union[pathlib.Path, str]] = None,
476        /,
477    ) -> None:
478        self._set_debug_level(level, file)

Enables debugging for the REST calls.

Logging Levels
  • False: This will disable logging.
  • True: This will set the level to DEBUG and enable logging minimal information.
  • "TRACE" | aiobungie.TRACE: This will log the response headers along with the minimal information.
Parameters
  • level (str | bool | int): The level of debugging to enable.
  • file (pathlib.Path | str | None): The file path to write the debug logs to. If provided.
@typing.final
async def static_request( self, method: Union[aiobungie.RequestMethod, str], path: str, *, auth: Optional[str] = None, json: Optional[dict[str, Any]] = None) -> Union[dict[str, Any], list[Any], bytes, int, bool, NoneType]:
480    @typing.final
481    async def static_request(
482        self,
483        method: typing.Union[RequestMethod, str],
484        path: str,
485        *,
486        auth: typing.Optional[str] = None,
487        json: typing.Optional[dict[str, typing.Any]] = None,
488    ) -> ResponseSig:
489        return await self._request(method, path, auth=auth, json=json)

Perform an HTTP request given a valid Bungie endpoint.

Parameters
  • method (aiobungie.RequestMethod | str): The request method, This may be GET, POST, PUT, etc.
  • path (str): The Bungie endpoint or path. A path must look something like this Destiny2/3/Profile/46111239123/...
  • auth (str | None): An optional bearer token for methods that requires OAuth2 Authorization header.
  • json (dict[str, typing.Any] | None): An optional JSON data to include in the request.
Returns
  • aiobungie.rest.ResponseSig: The response payload.
@typing.final
def build_oauth2_url( self, client_id: Optional[int] = None) -> Optional[aiobungie.builders.OAuthURL]:
491    @typing.final
492    def build_oauth2_url(
493        self, client_id: typing.Optional[int] = None
494    ) -> typing.Optional[builders.OAuthURL]:
495        client_id = client_id or self._client_id
496        if client_id is None:
497            return None
498
499        return builders.OAuthURL(client_id=client_id)

Builds an OAuth2 URL using the provided user REST/Base client secret/id.

You can't get the complete string URL by using .compile() method.

Parameters
  • client_id (int | None): An optional client id to provide, If left None it will roll back to the id passed to the RESTClient, If both is None this method will return None.
Returns
async def fetch_oauth2_tokens(self, code: str, /) -> aiobungie.builders.OAuth2Response:
726    async def fetch_oauth2_tokens(self, code: str, /) -> builders.OAuth2Response:
727
728        if not isinstance(self._client_id, int):
729            raise TypeError(
730                f"Expected (int) for client id but got {type(self._client_id).__qualname__}"  # type: ignore
731            )
732
733        if not isinstance(self._client_secret, str):
734            raise TypeError(
735                f"Expected (str) for client secret but got {type(self._client_secret).__qualname__}"  # type: ignore
736            )
737
738        headers = {
739            "client_secret": self._client_secret,
740        }
741
742        data = (
743            f"grant_type=authorization_code&code={code}"
744            f"&client_id={self._client_id}&client_secret={self._client_secret}"
745        )
746
747        response = await self._request(
748            RequestMethod.POST, "", headers=headers, data=data, oauth2=True
749        )
750        assert isinstance(response, dict)
751        return builders.OAuth2Response.build_response(response)

Makes a POST request and fetch the OAuth2 access_token and refresh token.

Parameters
  • code (str): The Authorization code received from the authorization endpoint found in the URL parameters.
Returns
Raises
async def refresh_access_token(self, refresh_token: str, /) -> aiobungie.builders.OAuth2Response:
753    async def refresh_access_token(
754        self, refresh_token: str, /
755    ) -> builders.OAuth2Response:
756        if not isinstance(self._client_id, int):
757            raise TypeError(
758                f"Expected (int) for client id but got {type(self._client_id).__qualname__}"  # type: ignore
759            )
760
761        if not isinstance(self._client_secret, str):
762            raise TypeError(
763                f"Expected (str) for client secret but got {type(self._client_secret).__qualname__}"  # type: ignore
764            )
765
766        data = {
767            "grant_type": "refresh_token",
768            "refresh_token": refresh_token,
769            "client_id": self._client_id,
770            "client_secret": self._client_secret,
771            "Content-Type": "application/x-www-form-urlencoded",
772        }
773
774        response = await self._request(RequestMethod.POST, "", data=data, oauth2=True)
775        assert isinstance(response, dict)
776        return builders.OAuth2Response.build_response(response)

Refresh OAuth2 access token given its refresh token.

Parameters
  • refresh_token (str): The refresh token.
Returns
async def fetch_bungie_user(self, id: int) -> dict[str, typing.Any]:
778    async def fetch_bungie_user(self, id: int) -> typedefs.JSONObject:
779        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
780        resp = await self._request(
781            RequestMethod.GET, f"User/GetBungieNetUserById/{id}/"
782        )
783        assert isinstance(resp, dict)
784        return resp

Fetch a Bungie user by their id.

Parameters
  • id (int): The user id.
Returns
Raises
async def fetch_user_themes(self) -> list[typing.Any]:
786    async def fetch_user_themes(self) -> typedefs.JSONArray:
787        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
788        resp = await self._request(RequestMethod.GET, "User/GetAvailableThemes/")
789        assert isinstance(resp, list)
790        return resp

Fetch all available user themes.

Returns
async def fetch_membership_from_id( self, id: int, type: Union[int, aiobungie.MembershipType] = <MembershipType.NONE: 0>, /) -> dict[str, typing.Any]:
792    async def fetch_membership_from_id(
793        self,
794        id: int,
795        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.NONE,
796        /,
797    ) -> typedefs.JSONObject:
798        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
799        resp = await self._request(
800            RequestMethod.GET, f"User/GetMembershipsById/{id}/{int(type)}"
801        )
802        assert isinstance(resp, dict)
803        return resp

Fetch Bungie user's memberships from their id.

Parameters
Returns
Raises
async def fetch_player( self, name: str, code: int, type: Union[int, aiobungie.MembershipType] = <MembershipType.ALL: -1>, /) -> list[typing.Any]:
805    async def fetch_player(
806        self,
807        name: str,
808        code: int,
809        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.ALL,
810        /,
811    ) -> typedefs.JSONArray:
812        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
813        resp = await self._request(
814            RequestMethod.POST,
815            f"Destiny2/SearchDestinyPlayerByBungieName/{int(type)}",
816            json={"displayName": name, "displayNameCode": code},
817        )
818        assert isinstance(resp, list)
819        return resp

Fetch a Destiny 2 Player.

Parameters
Returns
Raises
async def search_users(self, name: str, /) -> dict[str, typing.Any]:
821    async def search_users(self, name: str, /) -> typedefs.JSONObject:
822        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
823        resp = await self._request(
824            RequestMethod.POST,
825            "User/Search/GlobalName/0",
826            json={"displayNamePrefix": name},
827        )
828        assert isinstance(resp, dict)
829        return resp

Search for users by their global name and return all users who share this name.

Parameters
  • name (str): The user name.
Returns
Raises
async def fetch_clan_from_id( self, id: int, /, access_token: Optional[str] = None) -> dict[str, typing.Any]:
831    async def fetch_clan_from_id(
832        self, id: int, /, access_token: typing.Optional[str] = None
833    ) -> typedefs.JSONObject:
834        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
835        resp = await self._request(
836            RequestMethod.GET, f"GroupV2/{id}", auth=access_token
837        )
838        assert isinstance(resp, dict)
839        return resp

Fetch a Bungie Clan by its id.

Parameters
  • id (int): The clan id.
Other Parameters
  • access_token (typing.Optional[str]): An optional access token to make the request with.

    If the token was bound to a member of the clan, This field aiobungie.crates.Clan.current_user_membership will be available and will return the membership of the user who made this request.

Returns
Raises
async def fetch_clan( self, name: str, /, access_token: Optional[str] = None, *, type: Union[int, aiobungie.GroupType] = <GroupType.CLAN: 1>) -> dict[str, typing.Any]:
841    async def fetch_clan(
842        self,
843        name: str,
844        /,
845        access_token: typing.Optional[str] = None,
846        *,
847        type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
848    ) -> typedefs.JSONObject:
849        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
850        resp = await self._request(
851            RequestMethod.GET, f"GroupV2/Name/{name}/{int(type)}", auth=access_token
852        )
853        assert isinstance(resp, dict)
854        return resp

Fetch a Clan by its name. This method will return the first clan found with given name name.

Parameters
  • name (str): The clan name.
Other Parameters
Returns
Raises
async def fetch_clan_admins(self, clan_id: int, /) -> dict[str, typing.Any]:
856    async def fetch_clan_admins(self, clan_id: int, /) -> typedefs.JSONObject:
857        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
858        resp = await self._request(
859            RequestMethod.GET, f"GroupV2/{clan_id}/AdminsAndFounder/"
860        )
861        assert isinstance(resp, dict)
862        return resp

Fetch the admins and founder members of the clan.

Parameters
  • clan_id (int): The clan id.
Returns
Raises
async def fetch_clan_conversations(self, clan_id: int, /) -> list[typing.Any]:
864    async def fetch_clan_conversations(self, clan_id: int, /) -> typedefs.JSONArray:
865        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
866        resp = await self._request(
867            RequestMethod.GET, f"GroupV2/{clan_id}/OptionalConversations/"
868        )
869        assert isinstance(resp, list)
870        return resp

Fetch a clan's conversations.

Parameters
  • clan_id (int): The clan's id.
Returns
async def fetch_application(self, appid: int, /) -> dict[str, typing.Any]:
872    async def fetch_application(self, appid: int, /) -> typedefs.JSONObject:
873        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
874        resp = await self._request(RequestMethod.GET, f"App/Application/{appid}")
875        assert isinstance(resp, dict)
876        return resp

Fetch a Bungie Application.

Parameters
  • appid (int): The application id.
Returns
async def fetch_character( self, member_id: int, membership_type: Union[int, aiobungie.MembershipType], character_id: int, components: list[aiobungie.ComponentType], auth: Optional[str] = None) -> dict[str, typing.Any]:
878    async def fetch_character(
879        self,
880        member_id: int,
881        membership_type: typedefs.IntAnd[enums.MembershipType],
882        character_id: int,
883        components: list[enums.ComponentType],
884        auth: typing.Optional[str] = None,
885    ) -> typedefs.JSONObject:
886        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
887        collector = _collect_components(components)
888        response = await self._request(
889            RequestMethod.GET,
890            f"Destiny2/{int(membership_type)}/Profile/{member_id}/"
891            f"Character/{character_id}/?components={collector}",
892            auth=auth,
893        )
894        assert isinstance(response, dict)
895        return response

Fetch a Destiny 2 player's characters.

Parameters
Other Parameters
  • auth (typing.Optional[str]): A bearer access_token to make the request with. This is optional and limited to components that only requires an Authorization token.
Returns
Raises
async def fetch_activities( self, member_id: int, character_id: int, mode: Union[int, aiobungie.GameMode], membership_type: Union[int, aiobungie.MembershipType] = <MembershipType.ALL: -1>, *, page: int = 0, limit: int = 1) -> dict[str, typing.Any]:
897    async def fetch_activities(
898        self,
899        member_id: int,
900        character_id: int,
901        mode: typedefs.IntAnd[enums.GameMode],
902        membership_type: typedefs.IntAnd[
903            enums.MembershipType
904        ] = enums.MembershipType.ALL,
905        *,
906        page: int = 0,
907        limit: int = 1,
908    ) -> typedefs.JSONObject:
909        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
910        resp = await self._request(
911            RequestMethod.GET,
912            f"Destiny2/{int(membership_type)}/Account/"
913            f"{member_id}/Character/{character_id}/Stats/Activities"
914            f"/?mode={int(mode)}&count={limit}&page={page}",
915        )
916        assert isinstance(resp, dict)
917        return resp

Fetch a Destiny 2 activity for the specified user id and character.

Parameters
  • member_id (int): The user id that starts with 4611.
  • character_id (int): The id of the character to retrieve.
  • mode (aiobungie.typedefs.IntAnd[aiobungie.GameMode]): This parameter filters the game mode, Nightfall, Strike, Iron Banner, etc.
Other Parameters
Returns
Raises
async def fetch_vendor_sales(self) -> dict[str, typing.Any]:
919    async def fetch_vendor_sales(self) -> typedefs.JSONObject:
920        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
921        resp = await self._request(
922            RequestMethod.GET,
923            f"Destiny2/Vendors/?components={int(enums.ComponentType.VENDOR_SALES)}",
924        )
925        assert isinstance(resp, dict)
926        return resp
async def fetch_profile( self, membership_id: int, type: Union[int, aiobungie.MembershipType], components: list[aiobungie.ComponentType], auth: Optional[str] = None) -> dict[str, typing.Any]:
928    async def fetch_profile(
929        self,
930        membership_id: int,
931        type: typedefs.IntAnd[enums.MembershipType],
932        components: list[enums.ComponentType],
933        auth: typing.Optional[str] = None,
934    ) -> typedefs.JSONObject:
935        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
936        collector = _collect_components(components)
937        response = await self._request(
938            RequestMethod.GET,
939            f"Destiny2/{int(type)}/Profile/{membership_id}/?components={collector}",
940            auth=auth,
941        )
942        assert isinstance(response, dict)
943        return response

Fetch a bungie profile.

Parameters
Other Parameters
  • auth (typing.Optional[str]): A bearer access_token to make the request with. This is optional and limited to components that only requires an Authorization token.
Returns
Raises
async def fetch_entity(self, type: str, hash: int) -> dict[str, typing.Any]:
945    async def fetch_entity(self, type: str, hash: int) -> typedefs.JSONObject:
946        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
947        response = await self._request(
948            RequestMethod.GET, route=f"Destiny2/Manifest/{type}/{hash}"
949        )
950        assert isinstance(response, dict)
951        return response

Fetch a Destiny definition item given its type and hash.

Parameters
  • type (str): Entity's type definition.
  • hash (int): Entity's hash.
Returns
async def fetch_inventory_item(self, hash: int, /) -> dict[str, typing.Any]:
953    async def fetch_inventory_item(self, hash: int, /) -> typedefs.JSONObject:
954        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
955        resp = await self.fetch_entity("DestinyInventoryItemDefinition", hash)
956        assert isinstance(resp, dict)
957        return resp

Fetch a Destiny inventory item entity given a its hash.

Parameters
  • hash (int): Entity's hash.
Returns
async def fetch_objective_entity(self, hash: int, /) -> dict[str, typing.Any]:
959    async def fetch_objective_entity(self, hash: int, /) -> typedefs.JSONObject:
960        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
961        resp = await self.fetch_entity("DestinyObjectiveDefinition", hash)
962        assert isinstance(resp, dict)
963        return resp

Fetch a Destiny objective entity given a its hash.

Parameters
  • hash (int): objective's hash.
Returns
async def fetch_groups_for_member( self, member_id: int, member_type: Union[int, aiobungie.MembershipType], /, *, filter: int = 0, group_type: Union[int, aiobungie.GroupType] = <GroupType.CLAN: 1>) -> dict[str, typing.Any]:
965    async def fetch_groups_for_member(
966        self,
967        member_id: int,
968        member_type: typedefs.IntAnd[enums.MembershipType],
969        /,
970        *,
971        filter: int = 0,
972        group_type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
973    ) -> typedefs.JSONObject:
974        resp = await self._request(
975            RequestMethod.GET,
976            f"GroupV2/User/{int(member_type)}/{member_id}/{filter}/{int(group_type)}/",
977        )
978        assert isinstance(resp, dict)
979        return resp

Fetch the information about the groups for a member.

Parameters
Other Parameters
Returns
async def fetch_potential_groups_for_member( self, member_id: int, member_type: Union[int, aiobungie.MembershipType], /, *, filter: int = 0, group_type: Union[int, aiobungie.GroupType] = <GroupType.CLAN: 1>) -> dict[str, typing.Any]:
981    async def fetch_potential_groups_for_member(
982        self,
983        member_id: int,
984        member_type: typedefs.IntAnd[enums.MembershipType],
985        /,
986        *,
987        filter: int = 0,
988        group_type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
989    ) -> typedefs.JSONObject:
990        resp = await self._request(
991            RequestMethod.GET,
992            f"GroupV2/User/Potential/{int(member_type)}/{member_id}/{filter}/{int(group_type)}/",
993        )
994        assert isinstance(resp, dict)
995        return resp

Get information about the groups that a given member has applied to or been invited to.

Parameters
Other Parameters
Returns
async def fetch_clan_members( self, clan_id: int, /, *, name: Optional[str] = None, type: Union[int, aiobungie.MembershipType] = <MembershipType.NONE: 0>) -> dict[str, typing.Any]:
 997    async def fetch_clan_members(
 998        self,
 999        clan_id: int,
1000        /,
1001        *,
1002        name: typing.Optional[str] = None,
1003        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.NONE,
1004    ) -> typedefs.JSONObject:
1005        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1006        resp = await self._request(
1007            RequestMethod.GET,
1008            f"/GroupV2/{clan_id}/Members/?memberType={int(type)}&nameSearch={name if name else ''}&currentpage=1",
1009        )
1010        assert isinstance(resp, dict)
1011        return resp

Fetch all Bungie Clan members.

Parameters
  • clan_id (builsins.int): The clans id
Other Parameters
Returns
Raises
async def fetch_hardlinked_credentials( self, credential: int, type: Union[int, aiobungie.CredentialType] = <CredentialType.STEAMID: 12>, /) -> dict[str, typing.Any]:
1013    async def fetch_hardlinked_credentials(
1014        self,
1015        credential: int,
1016        type: typedefs.IntAnd[enums.CredentialType] = enums.CredentialType.STEAMID,
1017        /,
1018    ) -> typedefs.JSONObject:
1019        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1020        resp = await self._request(
1021            RequestMethod.GET,
1022            f"User/GetMembershipFromHardLinkedCredential/{int(type)}/{credential}/",
1023        )
1024        assert isinstance(resp, dict)
1025        return resp

Gets any hard linked membership given a credential.

Only works for credentials that are public just aiobungie.CredentialType.STEAMID right now. Cross Save aware.

Parameters
Returns
async def fetch_user_credentials(self, access_token: str, membership_id: int, /) -> list[typing.Any]:
1027    async def fetch_user_credentials(
1028        self, access_token: str, membership_id: int, /
1029    ) -> typedefs.JSONArray:
1030        resp = await self._request(
1031            RequestMethod.GET,
1032            f"User/GetCredentialTypesForTargetAccount/{membership_id}",
1033            auth=access_token,
1034        )
1035        assert isinstance(resp, list)
1036        return resp

Fetch an array of credential types attached to the requested account.

This method require OAuth2 Bearer access token.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • membership_id (int): The id of the membership to return.
Returns
Raises
async def insert_socket_plug( self, action_token: str, /, instance_id: int, plug: Union[aiobungie.builders.PlugSocketBuilder, dict[str, int]], character_id: int, membership_type: Union[int, aiobungie.MembershipType]) -> dict[str, typing.Any]:
1038    async def insert_socket_plug(
1039        self,
1040        action_token: str,
1041        /,
1042        instance_id: int,
1043        plug: typing.Union[builders.PlugSocketBuilder, dict[str, int]],
1044        character_id: int,
1045        membership_type: typedefs.IntAnd[enums.MembershipType],
1046    ) -> typedefs.JSONObject:
1047
1048        if isinstance(plug, builders.PlugSocketBuilder):
1049            plug = plug.collect()
1050
1051        body = {
1052            "actionToken": action_token,
1053            "itemInstanceId": instance_id,
1054            "plug": plug,
1055            "characterId": character_id,
1056            "membershipType": int(membership_type),
1057        }
1058        resp = await self._request(
1059            RequestMethod.POST, "Destiny2/Actions/Items/InsertSocketPlug", json=body
1060        )
1061        assert isinstance(resp, dict)
1062        return resp

Insert a plug into a socketed item.

OAuth2: AdvancedWriteActions scope is required

Parameters
  • action_token (str): Action token provided by the AwaGetActionToken API call.
  • instance_id (int): The item instance id that's plug inserted.
  • plug (typing.Union[aiobungie.builders.PlugSocketBuilder, dict[str, int]]): Either a PlugSocketBuilder object or a raw dict contains key, value for the plug entries.
Example
plug = (
    aiobungie.PlugSocketBuilder()
    .set_socket_array(0)
    .set_socket_index(0)
    .set_plug_item(3023847)
    .collect()
)
await insert_socket_plug_free(..., plug=plug)

character_id : int The character's id. membership_type : aiobungie.typedefs.IntAnd[aiobungie.MembershipType] The membership type.

Returns
Raises
async def insert_socket_plug_free( self, access_token: str, /, instance_id: int, plug: Union[aiobungie.builders.PlugSocketBuilder, dict[str, int]], character_id: int, membership_type: Union[int, aiobungie.MembershipType]) -> dict[str, typing.Any]:
1064    async def insert_socket_plug_free(
1065        self,
1066        access_token: str,
1067        /,
1068        instance_id: int,
1069        plug: typing.Union[builders.PlugSocketBuilder, dict[str, int]],
1070        character_id: int,
1071        membership_type: typedefs.IntAnd[enums.MembershipType],
1072    ) -> typedefs.JSONObject:
1073
1074        if isinstance(plug, builders.PlugSocketBuilder):
1075            plug = plug.collect()
1076
1077        body = {
1078            "itemInstanceId": instance_id,
1079            "plug": plug,
1080            "characterId": character_id,
1081            "membershipType": int(membership_type),
1082        }
1083        resp = await self._request(
1084            RequestMethod.POST,
1085            "Destiny2/Actions/Items/InsertSocketPlugFree",
1086            json=body,
1087            auth=access_token,
1088        )
1089        assert isinstance(resp, dict)
1090        return resp

Insert a plug into a socketed item. This doesn't require an Action token.

OAuth2: MoveEquipDestinyItems scope is required

Parameters
  • instance_id (int): The item instance id that's plug inserted.
  • plug (typing.Union[aiobungie.builders.PlugSocketBuilder, dict[str, int]]): Either a PlugSocketBuilder object or a raw dict contains key, value for the plug entries.
Example
plug = (
    aiobungie.PlugSocketBuilder()
    .set_socket_array(0)
    .set_socket_index(0)
    .set_plug_item(3023847)
    .collect()
)
await insert_socket_plug_free(..., plug=plug)

character_id : int The character's id. membership_type : aiobungie.typedefs.IntAnd[aiobungie.MembershipType] The membership type.

Returns
Raises
async def set_item_lock_state( self, access_token: str, state: bool, /, item_id: int, character_id: int, membership_type: Union[int, aiobungie.MembershipType]) -> int:
1092    async def set_item_lock_state(
1093        self,
1094        access_token: str,
1095        state: bool,
1096        /,
1097        item_id: int,
1098        character_id: int,
1099        membership_type: typedefs.IntAnd[enums.MembershipType],
1100    ) -> int:
1101        body = {
1102            "state": state,
1103            "itemId": item_id,
1104            "characterId": character_id,
1105            "membership_type": int(membership_type),
1106        }
1107        response = await self._request(
1108            RequestMethod.POST,
1109            "Destiny2/Actions/Items/SetLockState",
1110            json=body,
1111            auth=access_token,
1112        )
1113        assert isinstance(response, int)
1114        return response

Set the Lock State for an instanced item.

OAuth2: MoveEquipDestinyItems scope is required

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • state (bool): If True, The item will be locked, If False, The item will be unlocked.
  • item_id (int): The item id.
  • character_id (int): The character id.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The membership type for the associated account.
Returns
  • int: An integer represents whether the request was successful or failed.
Raises
async def set_quest_track_state( self, access_token: str, state: bool, /, item_id: int, character_id: int, membership_type: Union[int, aiobungie.MembershipType]) -> int:
1116    async def set_quest_track_state(
1117        self,
1118        access_token: str,
1119        state: bool,
1120        /,
1121        item_id: int,
1122        character_id: int,
1123        membership_type: typedefs.IntAnd[enums.MembershipType],
1124    ) -> int:
1125        body = {
1126            "state": state,
1127            "itemId": item_id,
1128            "characterId": character_id,
1129            "membership_type": int(membership_type),
1130        }
1131        response = await self._request(
1132            RequestMethod.POST,
1133            "Destiny2/Actions/Items/SetTrackedState",
1134            json=body,
1135            auth=access_token,
1136        )
1137        assert isinstance(response, int)
1138        return response

Set the Tracking State for an instanced Quest or Bounty.

OAuth2: MoveEquipDestinyItems scope is required

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • state (bool): If True, The item will be locked, If False, The item will be unlocked.
  • item_id (int): The item id.
  • character_id (int): The character id.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The membership type for the associated account.
Returns
  • int: An integer represents whether the request was successful or failed.
Raises
async def fetch_manifest_path(self) -> dict[str, typing.Any]:
1140    async def fetch_manifest_path(self) -> typedefs.JSONObject:
1141        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1142        path = await self._request(RequestMethod.GET, "Destiny2/Manifest")
1143        assert isinstance(path, dict)
1144        return path

Fetch the manifest JSON paths.

Returns
  • typedefs.JSONObject: The manifest JSON paths.
async def read_manifest_bytes(self, language: str = 'en', /) -> bytes:
1146    async def read_manifest_bytes(self, language: str = "en", /) -> bytes:
1147        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1148        _ensure_manifest_language(language)
1149
1150        content = await self.fetch_manifest_path()
1151        resp = await self._request(
1152            RequestMethod.GET,
1153            content["mobileWorldContentPaths"][language],
1154            unwrapping="read",
1155            base=True,
1156        )
1157        assert isinstance(resp, bytes)
1158        return resp

Read raw manifest SQLite database bytes response.

This method can be used to write the bytes to zipped file and then extract it to get the manifest content.

Parameters
  • language (str): The manifest database language bytes to get.
Returns
  • bytes: The bytes to read and write the manifest database.
async def download_manifest( self, language: str = 'en', name: str = 'manifest', path: Union[pathlib.Path, str] = '.', *, force: bool = False) -> None:
1160    async def download_manifest(
1161        self,
1162        language: str = "en",
1163        name: str = "manifest",
1164        path: typing.Union[pathlib.Path, str] = ".",
1165        *,
1166        force: bool = False,
1167    ) -> None:
1168        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1169        complete_path = _get_path(name, path, sql=True)
1170
1171        if complete_path.exists() and force:
1172            if force:
1173                _LOG.info(
1174                    f"Found manifest in {complete_path!s}. Forcing to Re-Download."
1175                )
1176                complete_path.unlink(missing_ok=True)
1177
1178                return await self.download_manifest(language, name, path, force=force)
1179
1180            else:
1181                raise FileExistsError(
1182                    "Manifest file already exists, "
1183                    "To force download, set the `force` parameter to `True`."
1184                )
1185
1186        _LOG.info(f"Downloading manifest. Location: {complete_path!s}")
1187        data_bytes = await self.read_manifest_bytes(language)
1188        await asyncio.get_running_loop().run_in_executor(
1189            None, _write_sqlite_bytes, data_bytes, path, name
1190        )

A helper method to download the manifest.

Note

This method downloads the sqlite database and not JSON. Use RESTInterface.download_json_manifest for the JSON version.

Parameters
  • language (str): The manifest language to download, Default is english.
  • path (str | pathlib.Path): The path to save the manifest sqlite database. Example "D:/", Default is the current directory.
  • name (str): The manifest database file name. Default is manifest
  • force (bool): Whether to force the download. Default is False. However if set to true the old file will get removed and a new one will being to download.
Returns
  • None
Raises
  • FileNotFoundError: If the manifest file exists and force is False.
  • ValueError: If the provided language was not recognized.
async def download_json_manifest( self, file_name: str = 'manifest', path: Union[str, pathlib.Path] = '.', language: str = 'en') -> None:
1192    async def download_json_manifest(
1193        self,
1194        file_name: str = "manifest",
1195        path: typing.Union[str, pathlib.Path] = ".",
1196        language: str = "en",
1197    ) -> None:
1198        _ensure_manifest_language(language)
1199
1200        _LOG.info(f"Downloading manifest JSON to {_get_path(file_name, path)!r}...")
1201
1202        content = await self.fetch_manifest_path()
1203        json_bytes = await self._request(
1204            RequestMethod.GET,
1205            content["jsonWorldContentPaths"][language],
1206            unwrapping="read",
1207            base=True,
1208        )
1209
1210        await asyncio.get_running_loop().run_in_executor(
1211            None, _write_json_bytes, json_bytes, file_name, path
1212        )
1213        _LOG.info("Finished downloading manifest JSON.")

Download the Bungie manifest json file.

Parameters
  • file_name (str): The file name to save the manifest json file. Default is manifest.
  • path (str | pathlib.Path): The path to save the manifest json file. Default is the current directory. Example "D:/"
  • language (str): The manifest database language bytes to get. Default is English.
async def fetch_manifest_version(self) -> str:
1215    async def fetch_manifest_version(self) -> str:
1216        return typing.cast(str, (await self.fetch_manifest_path())["version"])

Fetch the manifest version.

Returns
  • str: The manifest version.
async def fetch_linked_profiles( self, member_id: int, member_type: Union[int, aiobungie.MembershipType], /, *, all: bool = False) -> dict[str, typing.Any]:
1218    async def fetch_linked_profiles(
1219        self,
1220        member_id: int,
1221        member_type: typedefs.IntAnd[enums.MembershipType],
1222        /,
1223        *,
1224        all: bool = False,
1225    ) -> typedefs.JSONObject:
1226        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1227        resp = await self._request(
1228            RequestMethod.GET,
1229            f"Destiny2/{int(member_type)}/Profile/{member_id}/LinkedProfiles/?getAllMemberships={all}",
1230        )
1231        assert isinstance(resp, dict)
1232        return resp

Returns a summary information about all profiles linked to the requested member.

The passed membership id/type maybe a Bungie.Net membership or a Destiny memberships.

It will only return linked accounts whose linkages you are allowed to view.

Parameters
Other Parameters
  • all (bool): If provided and set to True, All memberships regardless of whether thry're obscured by overrides will be returned,

    If provided and set to False, Only available memberships will be returned. The default for this is False.

Returns
async def fetch_clan_banners(self) -> dict[str, typing.Any]:
1234    async def fetch_clan_banners(self) -> typedefs.JSONObject:
1235        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1236        resp = await self._request(
1237            RequestMethod.GET, "Destiny2/Clan/ClanBannerDictionary/"
1238        )
1239        assert isinstance(resp, dict)
1240        return resp

Fetch the values of the clan banners.

Returns
async def fetch_public_milestones(self) -> dict[str, typing.Any]:
1242    async def fetch_public_milestones(self) -> typedefs.JSONObject:
1243        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1244        resp = await self._request(RequestMethod.GET, "Destiny2/Milestones/")
1245        assert isinstance(resp, dict)
1246        return resp

Fetch the available milestones.

Returns
async def fetch_public_milestone_content(self, milestone_hash: int, /) -> dict[str, typing.Any]:
1248    async def fetch_public_milestone_content(
1249        self, milestone_hash: int, /
1250    ) -> typedefs.JSONObject:
1251        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1252        resp = await self._request(
1253            RequestMethod.GET, f"Destiny2/Milestones/{milestone_hash}/Content/"
1254        )
1255        assert isinstance(resp, dict)
1256        return resp

Fetch the milestone content given its hash.

Parameters
  • milestone_hash (int): The milestone hash.
Returns
async def fetch_current_user_memberships(self, access_token: str, /) -> dict[str, typing.Any]:
1258    async def fetch_current_user_memberships(
1259        self, access_token: str, /
1260    ) -> typedefs.JSONObject:
1261        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1262        resp = await self._request(
1263            RequestMethod.GET,
1264            "User/GetMembershipsForCurrentUser/",
1265            auth=access_token,
1266        )
1267        assert isinstance(resp, dict)
1268        return resp

Fetch a bungie user's accounts with the signed in user. This GET method requires a Bearer access token for the authorization.

This requires OAuth2 scope enabled and the valid Bearer access_token.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
Returns
async def equip_item( self, access_token: str, /, item_id: int, character_id: int, membership_type: Union[int, aiobungie.MembershipType]) -> None:
1270    async def equip_item(
1271        self,
1272        access_token: str,
1273        /,
1274        item_id: int,
1275        character_id: int,
1276        membership_type: typedefs.IntAnd[enums.MembershipType],
1277    ) -> None:
1278        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1279        payload = {
1280            "itemId": item_id,
1281            "characterId": character_id,
1282            "membershipType": int(membership_type),
1283        }
1284
1285        await self._request(
1286            RequestMethod.POST,
1287            "Destiny2/Actions/Items/EquipItem/",
1288            json=payload,
1289            auth=access_token,
1290        )

Equip an item to a character.

This requires the OAuth2: MoveEquipDestinyItems scope. Also You must have a valid Destiny account, and either be in a social space, in orbit or offline.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • item_id (int): The item id.
  • character_id (int): The character's id to equip the item to.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The membership type assocaiated with this player.
async def equip_items( self, access_token: str, /, item_ids: list[int], character_id: int, membership_type: Union[int, aiobungie.MembershipType]) -> None:
1292    async def equip_items(
1293        self,
1294        access_token: str,
1295        /,
1296        item_ids: list[int],
1297        character_id: int,
1298        membership_type: typedefs.IntAnd[enums.MembershipType],
1299    ) -> None:
1300        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1301        payload = {
1302            "itemIds": item_ids,
1303            "characterId": character_id,
1304            "membershipType": int(membership_type),
1305        }
1306        await self._request(
1307            RequestMethod.POST,
1308            "Destiny2/Actions/Items/EquipItems/",
1309            json=payload,
1310            auth=access_token,
1311        )

Equip multiple items to a character.

This requires the OAuth2: MoveEquipDestinyItems scope. Also You must have a valid Destiny account, and either be in a social space, in orbit or offline.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • item_ids (list[int]): A list of item ids.
  • character_id (int): The character's id to equip the item to.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The membership type assocaiated with this player.
async def ban_clan_member( self, access_token: str, /, group_id: int, membership_id: int, membership_type: Union[int, aiobungie.MembershipType], *, length: int = 0, comment: Union[aiobungie.UndefinedType, str] = UNDEFINED) -> None:
1313    async def ban_clan_member(
1314        self,
1315        access_token: str,
1316        /,
1317        group_id: int,
1318        membership_id: int,
1319        membership_type: typedefs.IntAnd[enums.MembershipType],
1320        *,
1321        length: int = 0,
1322        comment: undefined.UndefinedOr[str] = undefined.UNDEFINED,
1323    ) -> None:
1324        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1325        payload = {"comment": str(comment), "length": length}
1326        await self._request(
1327            RequestMethod.POST,
1328            f"GroupV2/{group_id}/Members/{int(membership_type)}/{membership_id}/Ban/",
1329            json=payload,
1330            auth=access_token,
1331        )

Bans a member from the clan.

This request requires OAuth2: oauth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group id.
  • membership_id (int): The member id to ban.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The member's membership type.
Other Parameters
async def unban_clan_member( self, access_token: str, /, group_id: int, membership_id: int, membership_type: Union[int, aiobungie.MembershipType]) -> None:
1333    async def unban_clan_member(
1334        self,
1335        access_token: str,
1336        /,
1337        group_id: int,
1338        membership_id: int,
1339        membership_type: typedefs.IntAnd[enums.MembershipType],
1340    ) -> None:
1341        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1342        await self._request(
1343            RequestMethod.POST,
1344            f"GroupV2/{group_id}/Members/{int(membership_type)}/{membership_id}/Unban/",
1345            auth=access_token,
1346        )

Unbans a member from the clan.

This request requires OAuth2: oauth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group id.
  • membership_id (int): The member id to unban.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The member's membership type.
async def kick_clan_member( self, access_token: str, /, group_id: int, membership_id: int, membership_type: Union[int, aiobungie.MembershipType]) -> dict[str, typing.Any]:
1348    async def kick_clan_member(
1349        self,
1350        access_token: str,
1351        /,
1352        group_id: int,
1353        membership_id: int,
1354        membership_type: typedefs.IntAnd[enums.MembershipType],
1355    ) -> typedefs.JSONObject:
1356        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1357        resp = await self._request(
1358            RequestMethod.POST,
1359            f"GroupV2/{group_id}/Members/{int(membership_type)}/{membership_id}/Kick/",
1360            auth=access_token,
1361        )
1362        assert isinstance(resp, dict)
1363        return resp

Kick a member from the clan.

This request requires OAuth2: oauth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group id.
  • membership_id (int): The member id to kick.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The member's membership type.
Returns
async def edit_clan( self, access_token: str, /, group_id: int, *, name: Optional[str] = None, about: Optional[str] = None, motto: Optional[str] = None, theme: Optional[str] = None, tags: Optional[collections.abc.Sequence[str]] = None, is_public: Optional[bool] = None, locale: Optional[str] = None, avatar_image_index: Optional[int] = None, membership_option: Union[NoneType, int, aiobungie.MembershipOption] = None, allow_chat: Optional[bool] = None, chat_security: Optional[Literal[0, 1]] = None, call_sign: Optional[str] = None, homepage: Optional[Literal[0, 1, 2]] = None, enable_invite_messaging_for_admins: Optional[bool] = None, default_publicity: Optional[Literal[0, 1, 2]] = None, is_public_topic_admin: Optional[bool] = None) -> None:
1365    async def edit_clan(
1366        self,
1367        access_token: str,
1368        /,
1369        group_id: int,
1370        *,
1371        name: typedefs.NoneOr[str] = None,
1372        about: typedefs.NoneOr[str] = None,
1373        motto: typedefs.NoneOr[str] = None,
1374        theme: typedefs.NoneOr[str] = None,
1375        tags: typedefs.NoneOr[collections.Sequence[str]] = None,
1376        is_public: typedefs.NoneOr[bool] = None,
1377        locale: typedefs.NoneOr[str] = None,
1378        avatar_image_index: typedefs.NoneOr[int] = None,
1379        membership_option: typedefs.NoneOr[
1380            typedefs.IntAnd[enums.MembershipOption]
1381        ] = None,
1382        allow_chat: typedefs.NoneOr[bool] = None,
1383        chat_security: typedefs.NoneOr[typing.Literal[0, 1]] = None,
1384        call_sign: typedefs.NoneOr[str] = None,
1385        homepage: typedefs.NoneOr[typing.Literal[0, 1, 2]] = None,
1386        enable_invite_messaging_for_admins: typedefs.NoneOr[bool] = None,
1387        default_publicity: typedefs.NoneOr[typing.Literal[0, 1, 2]] = None,
1388        is_public_topic_admin: typedefs.NoneOr[bool] = None,
1389    ) -> None:
1390        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1391        payload = {
1392            "name": name,
1393            "about": about,
1394            "motto": motto,
1395            "theme": theme,
1396            "tags": tags,
1397            "isPublic": is_public,
1398            "avatarImageIndex": avatar_image_index,
1399            "isPublicTopicAdminOnly": is_public_topic_admin,
1400            "allowChat": allow_chat,
1401            "chatSecurity": chat_security,
1402            "callsign": call_sign,
1403            "homepage": homepage,
1404            "enableInvitationMessagingForAdmins": enable_invite_messaging_for_admins,
1405            "defaultPublicity": default_publicity,
1406            "locale": locale,
1407        }
1408        if membership_option is not None:
1409            payload["membershipOption"] = int(membership_option)
1410
1411        await self._request(
1412            RequestMethod.POST,
1413            f"GroupV2/{group_id}/Edit",
1414            json=payload,
1415            auth=access_token,
1416        )

Edit a clan.

Notes
  • This request requires OAuth2: oauth2: AdminGroups scope.
  • All arguments will default to None if not provided. This does not include access_token and group_id
Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group id to edit.
Other Parameters
async def edit_clan_options( self, access_token: str, /, group_id: int, *, invite_permissions_override: Optional[bool] = None, update_culture_permissionOverride: Optional[bool] = None, host_guided_game_permission_override: Optional[Literal[0, 1, 2]] = None, update_banner_permission_override: Optional[bool] = None, join_level: Union[NoneType, int, aiobungie.ClanMemberType] = None) -> None:
1418    async def edit_clan_options(
1419        self,
1420        access_token: str,
1421        /,
1422        group_id: int,
1423        *,
1424        invite_permissions_override: typedefs.NoneOr[bool] = None,
1425        update_culture_permissionOverride: typedefs.NoneOr[bool] = None,
1426        host_guided_game_permission_override: typedefs.NoneOr[
1427            typing.Literal[0, 1, 2]
1428        ] = None,
1429        update_banner_permission_override: typedefs.NoneOr[bool] = None,
1430        join_level: typedefs.NoneOr[typedefs.IntAnd[enums.ClanMemberType]] = None,
1431    ) -> None:
1432
1433        payload = {
1434            "InvitePermissionOverride": invite_permissions_override,
1435            "UpdateCulturePermissionOverride": update_culture_permissionOverride,
1436            "HostGuidedGamePermissionOverride": host_guided_game_permission_override,
1437            "UpdateBannerPermissionOverride": update_banner_permission_override,
1438            "JoinLevel": int(join_level) if join_level else None,
1439        }
1440
1441        await self._request(
1442            RequestMethod.POST,
1443            f"GroupV2/{group_id}/EditFounderOptions",
1444            json=payload,
1445            auth=access_token,
1446        )

Edit the clan options.

Notes
  • This request requires OAuth2: oauth2: AdminGroups scope.
  • All arguments will default to None if not provided. This does not include access_token and group_id
Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group id.
Other Parameters
  • invite_permissions_override (aiobungie.typedefs.NoneOr[bool]): Minimum Member Level allowed to invite new members to group Always Allowed: Founder, Acting Founder True means admins have this power, false means they don't Default is False for clans, True for groups.
  • update_culture_permissionOverride (aiobungie.typedefs.NoneOr[bool]): Minimum Member Level allowed to update group culture Always Allowed: Founder, Acting Founder True means admins have this power, false means they don't Default is False for clans, True for groups.
  • host_guided_game_permission_override (aiobungie.typedefs.NoneOr[typing.Literal[0, 1, 2]]): Minimum Member Level allowed to host guided games Always Allowed: Founder, Acting Founder, Admin Allowed Overrides: 0 -> None, 1 -> Beginner 2 -> Member. Default is Member for clans, None for groups, although this means nothing for groups.
  • update_banner_permission_override (aiobungie.typedefs.NoneOr[bool]): Minimum Member Level allowed to update banner Always Allowed: Founder, Acting Founder True means admins have this power, false means they don't Default is False for clans, True for groups.
  • join_level (aiobungie.ClanMemberType): Level to join a member at when accepting an invite, application, or joining an open clan. Default is aiobungie.ClanMemberType.BEGINNER
async def fetch_friends(self, access_token: str, /) -> dict[str, typing.Any]:
1448    async def fetch_friends(self, access_token: str, /) -> typedefs.JSONObject:
1449        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1450        resp = await self._request(
1451            RequestMethod.GET,
1452            "Social/Friends/",
1453            auth=access_token,
1454        )
1455        assert isinstance(resp, dict)
1456        return resp

Fetch bungie friend list.

This requests OAuth2: ReadUserData scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
Returns
async def fetch_friend_requests(self, access_token: str, /) -> dict[str, typing.Any]:
1458    async def fetch_friend_requests(self, access_token: str, /) -> typedefs.JSONObject:
1459        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1460        resp = await self._request(
1461            RequestMethod.GET,
1462            "Social/Friends/Requests",
1463            auth=access_token,
1464        )
1465        assert isinstance(resp, dict)
1466        return resp

Fetch pending bungie friend requests queue.

This requests OAuth2: ReadUserData scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
Returns
async def accept_friend_request(self, access_token: str, /, member_id: int) -> None:
1468    async def accept_friend_request(self, access_token: str, /, member_id: int) -> None:
1469        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1470        await self._request(
1471            RequestMethod.POST,
1472            f"Social/Friends/Requests/Accept/{member_id}",
1473            auth=access_token,
1474        )

Accepts a friend relationship with the target user. The user must be on your incoming friend request list.

This request requires OAuth2: BnetWrite scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • member_id (int): The member's id to accept.
async def send_friend_request(self, access_token: str, /, member_id: int) -> None:
1476    async def send_friend_request(self, access_token: str, /, member_id: int) -> None:
1477        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1478        await self._request(
1479            RequestMethod.POST,
1480            f"Social/Friends/Add/{member_id}",
1481            auth=access_token,
1482        )

Requests a friend relationship with the target user.

This request requires OAuth2: BnetWrite scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • member_id (int): The member's id to send the request to.
async def decline_friend_request(self, access_token: str, /, member_id: int) -> None:
1484    async def decline_friend_request(
1485        self, access_token: str, /, member_id: int
1486    ) -> None:
1487        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1488        await self._request(
1489            RequestMethod.POST,
1490            f"Social/Friends/Requests/Decline/{member_id}",
1491            auth=access_token,
1492        )

Decline a friend request with the target user. The user must be in your incoming friend request list.

This request requires OAuth2: BnetWrite scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • member_id (int): The member's id to decline.
async def remove_friend(self, access_token: str, /, member_id: int) -> None:
1494    async def remove_friend(self, access_token: str, /, member_id: int) -> None:
1495        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1496        await self._request(
1497            RequestMethod.POST,
1498            f"Social/Friends/Remove/{member_id}",
1499            auth=access_token,
1500        )

Removes a friend from your friend list. The user must be in your friend list.

This request requires OAuth2: BnetWrite scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • member_id (int): The member's id to remove.
async def remove_friend_request(self, access_token: str, /, member_id: int) -> None:
1502    async def remove_friend_request(self, access_token: str, /, member_id: int) -> None:
1503        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1504        await self._request(
1505            RequestMethod.POST,
1506            f"Social/Friends/Requests/Remove/{member_id}",
1507            auth=access_token,
1508        )

Removes a friend from your friend list requests. The user must be in your outgoing request list.

.. note : This request requires OAuth2: BnetWrite scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • member_id (int): The member's id to remove from the requested friend list.
async def approve_all_pending_group_users( self, access_token: str, /, group_id: int, message: Union[aiobungie.UndefinedType, str] = UNDEFINED) -> None:
1510    async def approve_all_pending_group_users(
1511        self,
1512        access_token: str,
1513        /,
1514        group_id: int,
1515        message: undefined.UndefinedOr[str] = undefined.UNDEFINED,
1516    ) -> None:
1517        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1518        await self._request(
1519            RequestMethod.POST,
1520            f"GroupV2/{group_id}/Members/ApproveAll",
1521            auth=access_token,
1522            json={"message": str(message)},
1523        )

Apporve all pending users for the given group id.

This request requires OAuth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The given group id.
Other Parameters
async def deny_all_pending_group_users( self, access_token: str, /, group_id: int, *, message: Union[aiobungie.UndefinedType, str] = UNDEFINED) -> None:
1525    async def deny_all_pending_group_users(
1526        self,
1527        access_token: str,
1528        /,
1529        group_id: int,
1530        *,
1531        message: undefined.UndefinedOr[str] = undefined.UNDEFINED,
1532    ) -> None:
1533        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1534        await self._request(
1535            RequestMethod.POST,
1536            f"GroupV2/{group_id}/Members/DenyAll",
1537            auth=access_token,
1538            json={"message": str(message)},
1539        )

Deny all pending users for the given group id.

This request requires OAuth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The given group id.
Other Parameters
async def add_optional_conversation( self, access_token: str, /, group_id: int, *, name: Union[aiobungie.UndefinedType, str] = UNDEFINED, security: Literal[0, 1] = 0) -> None:
1541    async def add_optional_conversation(
1542        self,
1543        access_token: str,
1544        /,
1545        group_id: int,
1546        *,
1547        name: undefined.UndefinedOr[str] = undefined.UNDEFINED,
1548        security: typing.Literal[0, 1] = 0,
1549    ) -> None:
1550        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1551        payload = {"chatName": str(name), "chatSecurity": security}
1552        await self._request(
1553            RequestMethod.POST,
1554            f"GroupV2/{group_id}/OptionalConversations/Add",
1555            json=payload,
1556            auth=access_token,
1557        )

Add a new chat channel to a group.

This request requires OAuth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The given group id.
Other parameters

name: aiobungie.UndefinedOr[str] The chat name. Default to UNDEFINED security: typing.Literal[0, 1] The security level of the chat.

If provided and set to 0, It will be to `Group` only.
If provided and set to 1, It will be `Admins` only.
Default is `0`
async def edit_optional_conversation( self, access_token: str, /, group_id: int, conversation_id: int, *, name: Union[aiobungie.UndefinedType, str] = UNDEFINED, security: Literal[0, 1] = 0, enable_chat: bool = False) -> None:
1559    async def edit_optional_conversation(
1560        self,
1561        access_token: str,
1562        /,
1563        group_id: int,
1564        conversation_id: int,
1565        *,
1566        name: undefined.UndefinedOr[str] = undefined.UNDEFINED,
1567        security: typing.Literal[0, 1] = 0,
1568        enable_chat: bool = False,
1569    ) -> None:
1570        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1571        payload = {
1572            "chatEnabled": enable_chat,
1573            "chatName": str(name),
1574            "chatSecurity": security,
1575        }
1576        await self._request(
1577            RequestMethod.POST,
1578            f"GroupV2/{group_id}/OptionalConversations/Edit/{conversation_id}",
1579            json=payload,
1580            auth=access_token,
1581        )

Edit the settings of this chat channel.

This request requires OAuth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The given group id.
  • conversation_id (int): The conversation/chat id.
Other parameters

name: aiobungie.UndefinedOr[str] The new chat name. Default to UNDEFINED security: typing.Literal[0, 1] The new security level of the chat.

If provided and set to 0, It will be to `Group` only.
If provided and set to 1, It will be `Admins` only.
Default is `0`

enable_chat : bool Whether to enable chatting or not. If set to True then chatting will be enabled. Otherwise it will be disabled.

async def transfer_item( self, access_token: str, /, item_id: int, item_hash: int, character_id: int, member_type: Union[int, aiobungie.MembershipType], *, stack_size: int = 1, vault: bool = False) -> None:
1583    async def transfer_item(
1584        self,
1585        access_token: str,
1586        /,
1587        item_id: int,
1588        item_hash: int,
1589        character_id: int,
1590        member_type: typedefs.IntAnd[enums.MembershipType],
1591        *,
1592        stack_size: int = 1,
1593        vault: bool = False,
1594    ) -> None:
1595        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1596        payload = {
1597            "characterId": character_id,
1598            "membershipType": int(member_type),
1599            "itemId": item_id,
1600            "itemReferenceHash": item_hash,
1601            "stackSize": stack_size,
1602            "transferToVault": vault,
1603        }
1604        await self._request(
1605            RequestMethod.POST,
1606            "Destiny2/Actions/Items/TransferItem",
1607            json=payload,
1608            auth=access_token,
1609        )

Transfer an item from / to your vault.

Notes
  • This method requires OAuth2: MoveEquipDestinyItems scope.
  • This method requires both item id and hash.
Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • item_id (int): The item instance id you to transfer.
  • item_hash (int): The item hash.
  • character_id (int): The character id to transfer the item from/to.
  • member_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The user membership type.
Other Parameters
  • stack_size (int): The item stack size.
  • valut (bool): Whether to trasnfer this item to your valut or not. Defaults to False.
async def pull_item( self, access_token: str, /, item_id: int, item_hash: int, character_id: int, member_type: Union[int, aiobungie.MembershipType], *, stack_size: int = 1, vault: bool = False) -> None:
1611    async def pull_item(
1612        self,
1613        access_token: str,
1614        /,
1615        item_id: int,
1616        item_hash: int,
1617        character_id: int,
1618        member_type: typedefs.IntAnd[enums.MembershipType],
1619        *,
1620        stack_size: int = 1,
1621        vault: bool = False,
1622    ) -> None:
1623        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1624        payload = {
1625            "characterId": character_id,
1626            "membershipType": int(member_type),
1627            "itemId": item_id,
1628            "itemReferenceHash": item_hash,
1629            "stackSize": stack_size,
1630            "transferToVault": vault,
1631        }
1632        await self._request(
1633            RequestMethod.POST,
1634            "Destiny2/Actions/Items/PullFromPostmaster",
1635            json=payload,
1636            auth=access_token,
1637        )

pull an item from the postmaster.

Notes
  • This method requires OAuth2: MoveEquipDestinyItems scope.
  • This method requires both item id and hash.
Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • item_id (int): The item instance id to pull.
  • item_hash (int): The item hash.
  • character_id (int): The character id to pull the item to.
  • member_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The user membership type.
Other Parameters
  • stack_size (int): The item stack size.
  • valut (bool): Whether to pill this item to your valut or not. Defaults to False.
async def fetch_fireteams( self, activity_type: Union[int, aiobungie.FireteamActivity], *, platform: Union[int, aiobungie.FireteamPlatform] = <FireteamPlatform.ANY: 0>, language: Union[aiobungie.FireteamLanguage, str] = <FireteamLanguage.ALL: >, date_range: Union[int, aiobungie.FireteamDate] = <FireteamDate.ALL: 0>, page: int = 0, slots_filter: int = 0) -> dict[str, typing.Any]:
1639    async def fetch_fireteams(
1640        self,
1641        activity_type: typedefs.IntAnd[fireteams.FireteamActivity],
1642        *,
1643        platform: typedefs.IntAnd[
1644            fireteams.FireteamPlatform
1645        ] = fireteams.FireteamPlatform.ANY,
1646        language: typing.Union[
1647            fireteams.FireteamLanguage, str
1648        ] = fireteams.FireteamLanguage.ALL,
1649        date_range: typedefs.IntAnd[
1650            fireteams.FireteamDate
1651        ] = fireteams.FireteamDate.ALL,
1652        page: int = 0,
1653        slots_filter: int = 0,
1654    ) -> typedefs.JSONObject:
1655        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1656        resp = await self._request(
1657            RequestMethod.GET,
1658            f"Fireteam/Search/Available/{int(platform)}/{int(activity_type)}/{int(date_range)}/{slots_filter}/{page}/?langFilter={str(language)}",  # noqa: E501 Line too long
1659        )
1660        assert isinstance(resp, dict)
1661        return resp

Fetch public Bungie fireteams with open slots.

Parameters
Other Parameters
Returns
async def fetch_avaliable_clan_fireteams( self, access_token: str, group_id: int, activity_type: Union[int, aiobungie.FireteamActivity], *, platform: Union[int, aiobungie.FireteamPlatform], language: Union[aiobungie.FireteamLanguage, str], date_range: Union[int, aiobungie.FireteamDate] = <FireteamDate.ALL: 0>, page: int = 0, public_only: bool = False, slots_filter: int = 0) -> dict[str, typing.Any]:
1663    async def fetch_avaliable_clan_fireteams(
1664        self,
1665        access_token: str,
1666        group_id: int,
1667        activity_type: typedefs.IntAnd[fireteams.FireteamActivity],
1668        *,
1669        platform: typedefs.IntAnd[fireteams.FireteamPlatform],
1670        language: typing.Union[fireteams.FireteamLanguage, str],
1671        date_range: typedefs.IntAnd[
1672            fireteams.FireteamDate
1673        ] = fireteams.FireteamDate.ALL,
1674        page: int = 0,
1675        public_only: bool = False,
1676        slots_filter: int = 0,
1677    ) -> typedefs.JSONObject:
1678        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1679        resp = await self._request(
1680            RequestMethod.GET,
1681            f"Fireteam/Clan/{group_id}/Available/{int(platform)}/{int(activity_type)}/{int(date_range)}/{slots_filter}/{public_only}/{page}",  # noqa: E501
1682            json={"langFilter": str(language)},
1683            auth=access_token,
1684        )
1685        assert isinstance(resp, dict)
1686        return resp

Fetch a clan's fireteams with open slots.

This method requires OAuth2: ReadGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group/clan id of the fireteam.
  • activity_type (aiobungie.typedefs.IntAnd[aiobungie.crates.FireteamActivity]): The fireteam activity type.
Other Parameters
  • platform (aiobungie.typedefs.IntAnd[aiobungie.FireteamPlatform]): If this is provided. Then the results will be filtered with the given platform. Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
  • language (typing.Union[aiobungie.FireteamLanguage, str]): A locale language to filter the used language in that fireteam. Defaults to aiobungie.crates.FireteamLanguage.ALL
  • date_range (aiobungie.typedefs.IntAnd[aiobungie.FireteamDate]): An integer to filter the date range of the returned fireteams. Defaults to aiobungie.FireteamDate.ALL.
  • page (int): The page number. By default its 0 which returns all available activities.
  • public_only (bool): If set to True, Then only public fireteams will be returned.
  • slots_filter (int): Filter the returned fireteams based on available slots. Default is 0
Returns
async def fetch_clan_fireteam( self, access_token: str, fireteam_id: int, group_id: int) -> dict[str, typing.Any]:
1688    async def fetch_clan_fireteam(
1689        self, access_token: str, fireteam_id: int, group_id: int
1690    ) -> typedefs.JSONObject:
1691        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1692        resp = await self._request(
1693            RequestMethod.GET,
1694            f"Fireteam/Clan/{group_id}/Summary/{fireteam_id}",
1695            auth=access_token,
1696        )
1697        assert isinstance(resp, dict)
1698        return resp

Fetch a specific clan fireteam.

This method requires OAuth2: ReadGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group/clan id to fetch the fireteam from.
  • fireteam_id (int): The fireteam id to fetch.
Returns
async def fetch_my_clan_fireteams( self, access_token: str, group_id: int, *, include_closed: bool = True, platform: Union[int, aiobungie.FireteamPlatform], language: Union[aiobungie.FireteamLanguage, str], filtered: bool = True, page: int = 0) -> dict[str, typing.Any]:
1700    async def fetch_my_clan_fireteams(
1701        self,
1702        access_token: str,
1703        group_id: int,
1704        *,
1705        include_closed: bool = True,
1706        platform: typedefs.IntAnd[fireteams.FireteamPlatform],
1707        language: typing.Union[fireteams.FireteamLanguage, str],
1708        filtered: bool = True,
1709        page: int = 0,
1710    ) -> typedefs.JSONObject:
1711        payload = {"groupFilter": filtered, "langFilter": str(language)}
1712        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1713        resp = await self._request(
1714            RequestMethod.GET,
1715            f"Fireteam/Clan/{group_id}/My/{int(platform)}/{include_closed}/{page}",
1716            json=payload,
1717            auth=access_token,
1718        )
1719        assert isinstance(resp, dict)
1720        return resp

Fetch a clan's fireteams with open slots.

This method requires OAuth2: ReadGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group/clan id to fetch.
Other Parameters
  • include_closed (bool): If provided and set to True, It will also return closed fireteams. If provided and set to False, It will only return public fireteams. Default is True.
  • platform (aiobungie.typedefs.IntAnd[aiobungie.FireteamPlatform]): If this is provided. Then the results will be filtered with the given platform. Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
  • language (typing.Union[aiobungie.FireteamLanguage, str]): A locale language to filter the used language in that fireteam. Defaults to aiobungie.crates.FireteamLanguage.ALL
  • filtered (bool): If set to True, it will filter by clan. Otherwise not. Default is True.
  • page (int): The page number. By default its 0 which returns all available activities.
Returns
async def fetch_private_clan_fireteams(self, access_token: str, group_id: int, /) -> int:
1722    async def fetch_private_clan_fireteams(
1723        self, access_token: str, group_id: int, /
1724    ) -> int:
1725        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1726        resp = await self._request(
1727            RequestMethod.GET,
1728            f"Fireteam/Clan/{group_id}/ActiveCount",
1729            auth=access_token,
1730        )
1731        assert isinstance(resp, int)
1732        return resp

Fetch the active count of the clan fireteams that are only private.

This method requires OAuth2: ReadGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group/clan id.
Returns
  • int: The active fireteams count. Max value returned is 25.
async def fetch_post_activity(self, instance_id: int, /) -> dict[str, typing.Any]:
1734    async def fetch_post_activity(self, instance_id: int, /) -> typedefs.JSONObject:
1735        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1736        resp = await self._request(
1737            RequestMethod.GET, f"Destiny2/Stats/PostGameCarnageReport/{instance_id}"
1738        )
1739        assert isinstance(resp, dict)
1740        return resp

Fetch a post activity details.

Parameters
  • instance_id (int): The activity instance id.
Returns
async def search_entities( self, name: str, entity_type: str, *, page: int = 0) -> dict[str, typing.Any]:
1742    async def search_entities(
1743        self, name: str, entity_type: str, *, page: int = 0
1744    ) -> typedefs.JSONObject:
1745        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1746        resp = await self._request(
1747            RequestMethod.GET,
1748            f"Destiny2/Armory/Search/{entity_type}/{name}/",
1749            json={"page": page},
1750        )
1751        assert isinstance(resp, dict)
1752        return resp

Search for Destiny2 entities given a name and its type.

Parameters
  • name (str): The name of the entity, i.e., Thunderlord, One thousand voices.
  • entity_type (str): The type of the entity, AKA Definition, For an example DestinyInventoryItemDefinition
Other Parameters
  • page (int): An optional page to return. Default to 0.
Returns
async def fetch_unique_weapon_history( self, membership_id: int, character_id: int, membership_type: Union[int, aiobungie.MembershipType]) -> dict[str, typing.Any]:
1754    async def fetch_unique_weapon_history(
1755        self,
1756        membership_id: int,
1757        character_id: int,
1758        membership_type: typedefs.IntAnd[enums.MembershipType],
1759    ) -> typedefs.JSONObject:
1760        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1761        resp = await self._request(
1762            RequestMethod.GET,
1763            f"Destiny2/{int(membership_type)}/Account/{membership_id}/Character/{character_id}/Stats/UniqueWeapons/",
1764        )
1765        assert isinstance(resp, dict)
1766        return resp

Fetch details about unique weapon usage for a character. Includes all exotics.

Parameters
Returns
async def fetch_item( self, member_id: int, item_id: int, membership_type: Union[int, aiobungie.MembershipType], components: list[aiobungie.ComponentType]) -> dict[str, typing.Any]:
1768    async def fetch_item(
1769        self,
1770        member_id: int,
1771        item_id: int,
1772        membership_type: typedefs.IntAnd[enums.MembershipType],
1773        components: list[enums.ComponentType],
1774    ) -> typedefs.JSONObject:
1775        collector = _collect_components(components)
1776        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1777        resp = await self._request(
1778            RequestMethod.GET,
1779            f"Destiny2/{int(membership_type)}/Profile/{member_id}/Item/{item_id}/?components={collector}",
1780        )
1781        assert isinstance(resp, dict)
1782        return resp

Fetch an instanced Destiny 2 item's details.

Parameters
Returns
async def fetch_clan_weekly_rewards(self, clan_id: int, /) -> dict[str, typing.Any]:
1784    async def fetch_clan_weekly_rewards(self, clan_id: int, /) -> typedefs.JSONObject:
1785        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1786        resp = await self._request(
1787            RequestMethod.GET, f"Destiny2/Clan/{clan_id}/WeeklyRewardState/"
1788        )
1789        assert isinstance(resp, dict)
1790        return resp

Fetch the weekly reward state for a clan.

Parameters
  • clan_id (int): The clan id.
Returns
async def fetch_available_locales(self) -> dict[str, typing.Any]:
1792    async def fetch_available_locales(self) -> typedefs.JSONObject:
1793        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1794        resp = await self._request(
1795            RequestMethod.GET, "Destiny2/Manifest/DestinyLocaleDefinition/"
1796        )
1797        assert isinstance(resp, dict)
1798        return resp

Fetch available locales at Bungie.

Returns
async def fetch_common_settings(self) -> dict[str, typing.Any]:
1800    async def fetch_common_settings(self) -> typedefs.JSONObject:
1801        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1802        resp = await self._request(RequestMethod.GET, "Settings")
1803        assert isinstance(resp, dict)
1804        return resp

Fetch the common settings used by Bungie's envirotment.

Returns
async def fetch_user_systems_overrides(self) -> dict[str, typing.Any]:
1806    async def fetch_user_systems_overrides(self) -> typedefs.JSONObject:
1807        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1808        resp = await self._request(RequestMethod.GET, "UserSystemOverrides")
1809        assert isinstance(resp, dict)
1810        return resp

Fetch a user's specific system overrides.

Returns
async def fetch_global_alerts(self, *, include_streaming: bool = False) -> list[typing.Any]:
1812    async def fetch_global_alerts(
1813        self, *, include_streaming: bool = False
1814    ) -> typedefs.JSONArray:
1815        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1816        resp = await self._request(
1817            RequestMethod.GET, f"GlobalAlerts/?includestreaming={include_streaming}"
1818        )
1819        assert isinstance(resp, list)
1820        return resp

Fetch any active global alerts.

Parameters
  • include_streaming (bool): If True, the returned results will include streaming alerts. Default is False.
Returns
async def awainitialize_request( self, access_token: str, type: Literal[0, 1], membership_type: Union[int, aiobungie.MembershipType], /, *, affected_item_id: Optional[int] = None, character_id: Optional[int] = None) -> dict[str, typing.Any]:
1822    async def awainitialize_request(
1823        self,
1824        access_token: str,
1825        type: typing.Literal[0, 1],
1826        membership_type: typedefs.IntAnd[enums.MembershipType],
1827        /,
1828        *,
1829        affected_item_id: typing.Optional[int] = None,
1830        character_id: typing.Optional[int] = None,
1831    ) -> typedefs.JSONObject:
1832        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1833
1834        body = {"type": type, "membershipType": int(membership_type)}
1835
1836        if affected_item_id is not None:
1837            body["affectedItemId"] = affected_item_id
1838
1839        if character_id is not None:
1840            body["characterId"] = character_id
1841
1842        resp = await self._request(
1843            RequestMethod.POST, "Destiny2/Awa/Initialize", json=body, auth=access_token
1844        )
1845        assert isinstance(resp, dict)
1846        return resp

Initialize a request to perform an advanced write action.

OAuth2: AdvancedWriteActions application scope is required to perform this request.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • type (typing.Literal[0, 1]): Type of the advanced write action. Its either 0 or 1. If set to 0 that means it None. Otherwise if 1 that means its insert plugs.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The Destiny membership type of the account to modify.
Other Parameters
  • affected_item_id (typing.Optional[int]): Item instance ID the action shall be applied to. This is optional for all but a new AwaType values.
  • character_id (typing.Optional[int]): The Destiny character ID to perform this action on.
Returns
async def awaget_action_token(self, access_token: str, correlation_id: str, /) -> dict[str, typing.Any]:
1848    async def awaget_action_token(
1849        self, access_token: str, correlation_id: str, /
1850    ) -> typedefs.JSONObject:
1851        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1852        resp = await self._request(
1853            RequestMethod.POST,
1854            f"Destiny2/Awa/GetActionToken/{correlation_id}",
1855            auth=access_token,
1856        )
1857        assert isinstance(resp, dict)
1858        return resp

Returns the action token if user approves the request.

OAuth2: AdvancedWriteActions application scope is required to perform this request.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • correlation_id (str): The identifier for the advanced write action request.
Returns
async def awa_provide_authorization_result( self, access_token: str, selection: int, correlation_id: str, nonce: collections.abc.MutableSequence[typing.Union[str, bytes]]) -> int:
1860    async def awa_provide_authorization_result(
1861        self,
1862        access_token: str,
1863        selection: int,
1864        correlation_id: str,
1865        nonce: collections.MutableSequence[typing.Union[str, bytes]],
1866    ) -> int:
1867        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1868
1869        body = {"selection": selection, "correlationId": correlation_id, "nonce": nonce}
1870
1871        resp = await self._request(
1872            RequestMethod.POST,
1873            "Destiny2/Awa/AwaProvideAuthorizationResult",
1874            json=body,
1875            auth=access_token,
1876        )
1877        assert isinstance(resp, int)
1878        return resp

Provide the result of the user interaction. Called by the Bungie Destiny App to approve or reject a request.

OAuth2: AdvancedWriteActions application scope is required to perform this request.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • selection (int): Indication of the selection the user has made (Approving or rejecting the action)
  • correlation_id (str): Correlation ID of the request.
  • nonce (collections.MutableSequence[str, bytes]): Secret nonce received via the PUSH notification.
Returns
  • int: ...
async def fetch_vendors( self, access_token: str, character_id: int, membership_id: int, membership_type: Union[int, aiobungie.MembershipType], /, components: list[aiobungie.ComponentType], filter: Optional[int] = None) -> dict[str, typing.Any]:
1880    async def fetch_vendors(
1881        self,
1882        access_token: str,
1883        character_id: int,
1884        membership_id: int,
1885        membership_type: typedefs.IntAnd[enums.MembershipType],
1886        /,
1887        components: list[enums.ComponentType],
1888        filter: typing.Optional[int] = None,
1889    ) -> typedefs.JSONObject:
1890        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1891        components_ = _collect_components(components)
1892        route = (
1893            f"Destiny2/{int(membership_type)}/Profile/{membership_id}"
1894            f"/Character/{character_id}/Vendors/?components={components_}"
1895        )
1896
1897        if filter is not None:
1898            route = route + f"&filter={filter}"
1899
1900        resp = await self._request(
1901            RequestMethod.GET,
1902            route,
1903            auth=access_token,
1904        )
1905        assert isinstance(resp, dict)
1906        return resp

Get currently available vendors from the list of vendors that can possibly have rotating inventory.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • character_id (int): The character ID to return the vendor info for.
  • membership_id (int): The Destiny membership id to return the vendor info for.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The Destiny membership type to return the vendor info for.
  • components (list[aiobungie.ComponentType]): A list of vendor components to collect and return.
Other Parameters
  • filter (int): Filters the type of items returned from the vendor. This can be left to None.
Returns
async def fetch_vendor( self, access_token: str, character_id: int, membership_id: int, membership_type: Union[int, aiobungie.MembershipType], vendor_hash: int, /, components: list[aiobungie.ComponentType]) -> dict[str, typing.Any]:
1908    async def fetch_vendor(
1909        self,
1910        access_token: str,
1911        character_id: int,
1912        membership_id: int,
1913        membership_type: typedefs.IntAnd[enums.MembershipType],
1914        vendor_hash: int,
1915        /,
1916        components: list[enums.ComponentType],
1917    ) -> typedefs.JSONObject:
1918        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1919        components_ = _collect_components(components)
1920        resp = await self._request(
1921            RequestMethod.GET,
1922            (
1923                f"Platform/Destiny2/{int(membership_type)}/Profile/{membership_id}"
1924                f"/Character/{character_id}/Vendors/{vendor_hash}/?components={components_}"
1925            ),
1926            auth=access_token,
1927        )
1928        assert isinstance(resp, dict)
1929        return resp

Fetch details for a specific vendor.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • character_id (int): The character ID to return the vendor info for.
  • membership_id (int): The Destiny membership id to return the vendor info for.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The Destiny membership type to return the vendor info for.
  • vendor_hash (int): The vendor hash to return the details for.
  • components (list[aiobungie.ComponentType]): A list of vendor components to collect and return.
Returns
async def fetch_application_api_usage( self, access_token: str, application_id: int, /, *, start: Optional[datetime.datetime] = None, end: Optional[datetime.datetime] = None) -> dict[str, typing.Any]:
1931    async def fetch_application_api_usage(
1932        self,
1933        access_token: str,
1934        application_id: int,
1935        /,
1936        *,
1937        start: typing.Optional[datetime.datetime] = None,
1938        end: typing.Optional[datetime.datetime] = None,
1939    ) -> typedefs.JSONObject:
1940
1941        end_date, start_date = time.parse_date_range(end, start)
1942        resp = await self._request(
1943            RequestMethod.GET,
1944            f"App/ApiUsage/{application_id}/?end={end_date}&start={start_date}",
1945            auth=access_token,
1946        )
1947        assert isinstance(resp, dict)
1948        return resp

Fetch a Bungie application's API usage.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • application_id (int): The application id to get.
Other Parameters
  • start (typing.Optional[datetime.datetime]): A datetime object can be used to collect the start of the application usage. This is limited and can go back to 30 days maximum.

    If this is left to None. It will return the last 24 hours.

  • end (typing.Optional[datetime.datetime]): A datetime object can be used to collect the end of the application usage.

    If this is left to None. It will return now.

Example
import datetime

# Fetch data from 2021 Dec 10th to 2021 Dec 20th
await fetch_application_api_usage(
    start=datetime.datetime(2021, 12, 10), end=datetime.datetime(2021, 12, 20)
)
Returns
async def fetch_bungie_applications(self) -> list[typing.Any]:
1950    async def fetch_bungie_applications(self) -> typedefs.JSONArray:
1951        resp = await self._request(RequestMethod.GET, "App/FirstParty")
1952        assert isinstance(resp, list)
1953        return resp

Fetch details for applications created by Bungie.

Returns
async def fetch_content_type(self, type: str, /) -> dict[str, typing.Any]:
1955    async def fetch_content_type(self, type: str, /) -> typedefs.JSONObject:
1956        resp = await self._request(RequestMethod.GET, f"Content/GetContentType/{type}/")
1957        assert isinstance(resp, dict)
1958        return resp
async def fetch_content_by_id( self, id: int, locale: str, /, *, head: bool = False) -> dict[str, typing.Any]:
1960    async def fetch_content_by_id(
1961        self, id: int, locale: str, /, *, head: bool = False
1962    ) -> typedefs.JSONObject:
1963        resp = await self._request(
1964            RequestMethod.GET,
1965            f"Content/GetContentById/{id}/{locale}/",
1966            json={"head": head},
1967        )
1968        assert isinstance(resp, dict)
1969        return resp
async def fetch_content_by_tag_and_type( self, locale: str, tag: str, type: str, *, head: bool = False) -> dict[str, typing.Any]:
1971    async def fetch_content_by_tag_and_type(
1972        self, locale: str, tag: str, type: str, *, head: bool = False
1973    ) -> typedefs.JSONObject:
1974        resp = await self._request(
1975            RequestMethod.GET,
1976            f"Content/GetContentByTagAndType/{tag}/{type}/{locale}/",
1977            json={"head": head},
1978        )
1979        assert isinstance(resp, dict)
1980        return resp
async def search_content_with_text( self, locale: str, /, content_type: str, search_text: str, tag: str, *, page: Union[aiobungie.UndefinedType, int] = UNDEFINED, source: Union[aiobungie.UndefinedType, str] = UNDEFINED) -> dict[str, typing.Any]:
1982    async def search_content_with_text(
1983        self,
1984        locale: str,
1985        /,
1986        content_type: str,
1987        search_text: str,
1988        tag: str,
1989        *,
1990        page: undefined.UndefinedOr[int] = undefined.UNDEFINED,
1991        source: undefined.UndefinedOr[str] = undefined.UNDEFINED,
1992    ) -> typedefs.JSONObject:
1993
1994        body: typedefs.JSONObject = {}
1995
1996        body["ctype"] = content_type
1997        body["searchtext"] = search_text
1998        body["tag"] = tag
1999
2000        if page is not undefined.UNDEFINED:
2001            body["currentpage"] = page
2002        else:
2003            body["currentpage"] = 1
2004
2005        if source is not undefined.UNDEFINED:
2006            body["source"] = source
2007        else:
2008            source = ""
2009        resp = await self._request(
2010            RequestMethod.GET, f"Content/Search/{locale}/", json=body
2011        )
2012        assert isinstance(resp, dict)
2013        return resp
async def search_content_by_tag_and_type( self, locale: str, tag: str, type: str, *, page: Union[aiobungie.UndefinedType, int] = UNDEFINED) -> dict[str, typing.Any]:
2015    async def search_content_by_tag_and_type(
2016        self,
2017        locale: str,
2018        tag: str,
2019        type: str,
2020        *,
2021        page: undefined.UndefinedOr[int] = undefined.UNDEFINED,
2022    ) -> typedefs.JSONObject:
2023        body: typedefs.JSONObject = {}
2024        body["currentpage"] = 1 if page is undefined.UNDEFINED else page
2025        resp = await self._request(
2026            RequestMethod.GET,
2027            f"Content/SearchContentByTagAndType/{tag}/{type}/{locale}/",
2028            json=body,
2029        )
2030        assert isinstance(resp, dict)
2031        return resp
async def search_help_articles(self, text: str, size: str, /) -> dict[str, typing.Any]:
2033    async def search_help_articles(
2034        self, text: str, size: str, /
2035    ) -> typedefs.JSONObject:
2036        resp = await self._request(
2037            RequestMethod.GET, f"Content/SearchHelpArticles/{text}/{size}/"
2038        )
2039        assert isinstance(resp, dict)
2040        return resp
async def fetch_topics_page( self, category_filter: int, group: int, date_filter: int, sort: Union[str, bytes], *, page: Union[aiobungie.UndefinedType, int] = UNDEFINED, locales: Union[aiobungie.UndefinedType, collections.abc.Iterable[str]] = UNDEFINED, tag_filter: Union[aiobungie.UndefinedType, str] = UNDEFINED) -> dict[str, typing.Any]:
2042    async def fetch_topics_page(
2043        self,
2044        category_filter: int,
2045        group: int,
2046        date_filter: int,
2047        sort: typing.Union[str, bytes],
2048        *,
2049        page: undefined.UndefinedOr[int] = undefined.UNDEFINED,
2050        locales: undefined.UndefinedOr[collections.Iterable[str]] = undefined.UNDEFINED,
2051        tag_filter: undefined.UndefinedOr[str] = undefined.UNDEFINED,
2052    ) -> typedefs.JSONObject:
2053
2054        body: typedefs.JSONObject = {}
2055        if locales is not undefined.UNDEFINED:
2056            body["locales"] = ",".join(str(locales))
2057        else:
2058            body["locales"] = ",".join([])
2059
2060        if tag_filter is not undefined.UNDEFINED:
2061            body["tagstring"] = tag_filter
2062        else:
2063            body["tagstring"] = ""
2064
2065        page = 0 if page is not undefined.UNDEFINED else page
2066
2067        resp = await self._request(
2068            RequestMethod.GET,
2069            f"Forum/GetTopicsPaged/{page}/{0}/{group}/{sort!s}/{date_filter}/{category_filter}/",
2070            json=body,
2071        )
2072        assert isinstance(resp, dict)
2073        return resp
async def fetch_core_topics_page( self, category_filter: int, date_filter: int, sort: Union[str, bytes], *, page: Union[aiobungie.UndefinedType, int] = UNDEFINED, locales: Union[aiobungie.UndefinedType, collections.abc.Iterable[str]] = UNDEFINED) -> dict[str, typing.Any]:
2075    async def fetch_core_topics_page(
2076        self,
2077        category_filter: int,
2078        date_filter: int,
2079        sort: typing.Union[str, bytes],
2080        *,
2081        page: undefined.UndefinedOr[int] = undefined.UNDEFINED,
2082        locales: undefined.UndefinedOr[collections.Iterable[str]] = undefined.UNDEFINED,
2083    ) -> typedefs.JSONObject:
2084        body: typedefs.JSONObject = {}
2085
2086        if locales is not undefined.UNDEFINED:
2087            body["locales"] = ",".join(str(locales))
2088        else:
2089            body["locales"] = ",".join([])
2090
2091        resp = await self._request(
2092            RequestMethod.GET,
2093            f"Forum/GetCoreTopicsPaged/{0 if page is undefined.UNDEFINED else page}"
2094            f"/{sort!s}/{date_filter}/{category_filter}/",
2095            json=body,
2096        )
2097        assert isinstance(resp, dict)
2098        return resp
async def fetch_posts_threaded_page( self, parent_post: bool, page: int, page_size: int, parent_post_id: int, reply_size: int, root_thread_mode: bool, sort_mode: int, show_banned: Optional[str] = None) -> dict[str, typing.Any]:
2100    async def fetch_posts_threaded_page(
2101        self,
2102        parent_post: bool,
2103        page: int,
2104        page_size: int,
2105        parent_post_id: int,
2106        reply_size: int,
2107        root_thread_mode: bool,
2108        sort_mode: int,
2109        show_banned: typing.Optional[str] = None,
2110    ) -> typedefs.JSONObject:
2111        resp = await self._request(
2112            RequestMethod.GET,
2113            f"Forum/GetPostsThreadedPaged/{parent_post}/{page}/"
2114            f"{page_size}/{reply_size}/{parent_post_id}/{root_thread_mode}/{sort_mode}/",
2115            json={"showbanned": show_banned},
2116        )
2117        assert isinstance(resp, dict)
2118        return resp
async def fetch_posts_threaded_page_from_child( self, child_id: bool, page: int, page_size: int, reply_size: int, root_thread_mode: bool, sort_mode: int, show_banned: Optional[str] = None) -> dict[str, typing.Any]:
2120    async def fetch_posts_threaded_page_from_child(
2121        self,
2122        child_id: bool,
2123        page: int,
2124        page_size: int,
2125        reply_size: int,
2126        root_thread_mode: bool,
2127        sort_mode: int,
2128        show_banned: typing.Optional[str] = None,
2129    ) -> typedefs.JSONObject:
2130        resp = await self._request(
2131            RequestMethod.GET,
2132            f"Forum/GetPostsThreadedPagedFromChild/{child_id}/"
2133            f"{page}/{page_size}/{reply_size}/{root_thread_mode}/{sort_mode}/",
2134            json={"showbanned": show_banned},
2135        )
2136        assert isinstance(resp, dict)
2137        return resp
async def fetch_post_and_parent( self, child_id: int, /, *, show_banned: Optional[str] = None) -> dict[str, typing.Any]:
2139    async def fetch_post_and_parent(
2140        self, child_id: int, /, *, show_banned: typing.Optional[str] = None
2141    ) -> typedefs.JSONObject:
2142        resp = await self._request(
2143            RequestMethod.GET,
2144            f"Forum/GetPostAndParent/{child_id}/",
2145            json={"showbanned": show_banned},
2146        )
2147        assert isinstance(resp, dict)
2148        return resp
async def fetch_posts_and_parent_awaiting( self, child_id: int, /, *, show_banned: Optional[str] = None) -> dict[str, typing.Any]:
2150    async def fetch_posts_and_parent_awaiting(
2151        self, child_id: int, /, *, show_banned: typing.Optional[str] = None
2152    ) -> typedefs.JSONObject:
2153        resp = await self._request(
2154            RequestMethod.GET,
2155            f"Forum/GetPostAndParentAwaitingApproval/{child_id}/",
2156            json={"showbanned": show_banned},
2157        )
2158        assert isinstance(resp, dict)
2159        return resp
async def fetch_topic_for_content(self, content_id: int, /) -> int:
2161    async def fetch_topic_for_content(self, content_id: int, /) -> int:
2162        resp = await self._request(
2163            RequestMethod.GET, f"Forum/GetTopicForContent/{content_id}/"
2164        )
2165        assert isinstance(resp, int)
2166        return resp
async def fetch_forum_tag_suggestions(self, partial_tag: str, /) -> dict[str, typing.Any]:
2168    async def fetch_forum_tag_suggestions(
2169        self, partial_tag: str, /
2170    ) -> typedefs.JSONObject:
2171        resp = await self._request(
2172            RequestMethod.GET,
2173            "Forum/GetForumTagSuggestions/",
2174            json={"partialtag": partial_tag},
2175        )
2176        assert isinstance(resp, dict)
2177        return resp
async def fetch_poll(self, topic_id: int, /) -> dict[str, typing.Any]:
2179    async def fetch_poll(self, topic_id: int, /) -> typedefs.JSONObject:
2180        resp = await self._request(RequestMethod.GET, f"Forum/Poll/{topic_id}/")
2181        assert isinstance(resp, dict)
2182        return resp
async def fetch_recuirement_thread_summaries(self) -> list[typing.Any]:
2184    async def fetch_recuirement_thread_summaries(self) -> typedefs.JSONArray:
2185        resp = await self._request(RequestMethod.POST, "Forum/Recruit/Summaries/")
2186        assert isinstance(resp, list)
2187        return resp
async def fetch_available_avatars(self) -> collections.abc.Mapping[str, int]:
2205    async def fetch_available_avatars(self) -> collections.Mapping[str, int]:
2206        resp = await self._request(RequestMethod.GET, "GroupV2/GetAvailableAvatars/")
2207        assert isinstance(resp, dict)
2208        return resp
async def fetch_user_clan_invite_setting( self, access_token: str, /, membership_type: Union[int, aiobungie.MembershipType]) -> bool:
2210    async def fetch_user_clan_invite_setting(
2211        self,
2212        access_token: str,
2213        /,
2214        membership_type: typedefs.IntAnd[enums.MembershipType],
2215    ) -> bool:
2216        resp = await self._request(
2217            RequestMethod.GET,
2218            f"GroupV2/GetUserClanInviteSetting/{int(membership_type)}/",
2219            auth=access_token,
2220        )
2221        assert isinstance(resp, bool)
2222        return resp
async def fetch_banned_group_members( self, access_token: str, group_id: int, /, *, page: int = 1) -> dict[str, typing.Any]:
2224    async def fetch_banned_group_members(
2225        self, access_token: str, group_id: int, /, *, page: int = 1
2226    ) -> typedefs.JSONObject:
2227        resp = await self._request(
2228            RequestMethod.GET,
2229            f"GroupV2/{group_id}/Banned/?currentpage={page}",
2230            auth=access_token,
2231        )
2232        assert isinstance(resp, dict)
2233        return resp
async def fetch_pending_group_memberships( self, access_token: str, group_id: int, /, *, current_page: int = 1) -> dict[str, typing.Any]:
2235    async def fetch_pending_group_memberships(
2236        self, access_token: str, group_id: int, /, *, current_page: int = 1
2237    ) -> typedefs.JSONObject:
2238        resp = await self._request(
2239            RequestMethod.GET,
2240            f"GroupV2/{group_id}/Members/Pending/?currentpage={current_page}",
2241            auth=access_token,
2242        )
2243        assert isinstance(resp, dict)
2244        return resp
async def fetch_invited_group_memberships( self, access_token: str, group_id: int, /, *, current_page: int = 1) -> dict[str, typing.Any]:
2246    async def fetch_invited_group_memberships(
2247        self, access_token: str, group_id: int, /, *, current_page: int = 1
2248    ) -> typedefs.JSONObject:
2249        resp = await self._request(
2250            RequestMethod.GET,
2251            f"GroupV2/{group_id}/Members/InvitedIndividuals/?currentpage={current_page}",
2252            auth=access_token,
2253        )
2254        assert isinstance(resp, dict)
2255        return resp
async def invite_member_to_group( self, access_token: str, /, group_id: int, membership_id: int, membership_type: Union[int, aiobungie.MembershipType], *, message: Union[aiobungie.UndefinedType, str] = UNDEFINED) -> dict[str, typing.Any]:
2257    async def invite_member_to_group(
2258        self,
2259        access_token: str,
2260        /,
2261        group_id: int,
2262        membership_id: int,
2263        membership_type: typedefs.IntAnd[enums.MembershipType],
2264        *,
2265        message: undefined.UndefinedOr[str] = undefined.UNDEFINED,
2266    ) -> typedefs.JSONObject:
2267        resp = await self._request(
2268            RequestMethod.POST,
2269            f"GroupV2/{group_id}/Members/IndividualInvite/{int(membership_type)}/{membership_id}/",
2270            auth=access_token,
2271            json={"message": str(message)},
2272        )
2273        assert isinstance(resp, dict)
2274        return resp
async def cancel_group_member_invite( self, access_token: str, /, group_id: int, membership_id: int, membership_type: Union[int, aiobungie.MembershipType]) -> dict[str, typing.Any]:
2276    async def cancel_group_member_invite(
2277        self,
2278        access_token: str,
2279        /,
2280        group_id: int,
2281        membership_id: int,
2282        membership_type: typedefs.IntAnd[enums.MembershipType],
2283    ) -> typedefs.JSONObject:
2284        resp = await self._request(
2285            RequestMethod.POST,
2286            f"GroupV2/{group_id}/Members/IndividualInviteCancel/{int(membership_type)}/{membership_id}/",
2287            auth=access_token,
2288        )
2289        assert isinstance(resp, dict)
2290        return resp
async def fetch_historical_definition(self) -> dict[str, typing.Any]:
2292    async def fetch_historical_definition(self) -> typedefs.JSONObject:
2293        resp = await self._request(RequestMethod.GET, "Destiny2/Stats/Definition/")
2294        assert isinstance(resp, dict)
2295        return resp
async def fetch_historical_stats( self, character_id: int, membership_id: int, membership_type: Union[int, aiobungie.MembershipType], day_start: datetime.datetime, day_end: datetime.datetime, groups: list[typing.Union[int, aiobungie.internal.enums.StatsGroupType]], modes: collections.abc.Sequence[typing.Union[int, aiobungie.GameMode]], *, period_type: aiobungie.internal.enums.PeriodType = <PeriodType.ALL_TIME: 2>) -> dict[str, typing.Any]:
2297    async def fetch_historical_stats(
2298        self,
2299        character_id: int,
2300        membership_id: int,
2301        membership_type: typedefs.IntAnd[enums.MembershipType],
2302        day_start: datetime.datetime,
2303        day_end: datetime.datetime,
2304        groups: list[typedefs.IntAnd[enums.StatsGroupType]],
2305        modes: collections.Sequence[typedefs.IntAnd[enums.GameMode]],
2306        *,
2307        period_type: enums.PeriodType = enums.PeriodType.ALL_TIME,
2308    ) -> typedefs.JSONObject:
2309
2310        end, start = time.parse_date_range(day_end, day_start)
2311        resp = await self._request(
2312            RequestMethod.GET,
2313            f"Destiny2/{int(membership_type)}/Account/{membership_id}/Character/{character_id}/Stats/",
2314            json={
2315                "dayend": end,
2316                "daystart": start,
2317                "groups": [str(int(group)) for group in groups],
2318                "modes": [str(int(mode)) for mode in modes],
2319                "periodType": int(period_type),
2320            },
2321        )
2322        assert isinstance(resp, dict)
2323        return resp

Fetch historical stats for a specific membership character.

Parameters
  • character_id (int): The character ID to return the stats for.
  • membership_id (int): The Destiny membership id to return the stats for.
  • membership_type (aiobungie.MembershipType | int): The Destiny membership type to return the stats for.
  • day_start (datetime.datetime): The start of the day to return the stats for.
  • day_end (datetime.datetime): The end of the day to return the stats for.
  • groups (list[aiobungie.StatsGroupType]): A list of stats groups to return.
  • modes (list[aiobungie.GameMode | int]): A list of game modes to return.
  • period_type (aiobungie.enums.PeriodType): The period type to return the stats for. This will return ALL_TIME by default if not modified.
Returns
async def fetch_historical_stats_for_account( self, membership_id: int, membership_type: Union[int, aiobungie.MembershipType], groups: list[typing.Union[int, aiobungie.internal.enums.StatsGroupType]]) -> dict[str, typing.Any]:
2325    async def fetch_historical_stats_for_account(
2326        self,
2327        membership_id: int,
2328        membership_type: typedefs.IntAnd[enums.MembershipType],
2329        groups: list[typedefs.IntAnd[enums.StatsGroupType]],
2330    ) -> typedefs.JSONObject:
2331        resp = await self._request(
2332            RequestMethod.GET,
2333            f"Destiny2/{int(membership_type)}/Account/{membership_id}/Stats/",
2334            json={"groups": [str(int(group)) for group in groups]},
2335        )
2336        assert isinstance(resp, dict)
2337        return resp

Fetch historical stats for an account's membership.

Parameters
  • membership_id (int): The Destiny membership id to return the stats for.
  • membership_type (aiobungie.MembershipType | int): The Destiny membership type to return the stats for.
  • groups (list[aiobungie.StatsGroupType]): A list of stats groups to return.
Returns
async def fetch_aggregated_activity_stats( self, character_id: int, membership_id: int, membership_type: Union[int, aiobungie.MembershipType], /) -> dict[str, typing.Any]:
2339    async def fetch_aggregated_activity_stats(
2340        self,
2341        character_id: int,
2342        membership_id: int,
2343        membership_type: typedefs.IntAnd[enums.MembershipType],
2344        /,
2345    ) -> typedefs.JSONObject:
2346        resp = await self._request(
2347            RequestMethod.GET,
2348            f"Destiny2/{int(membership_type)}/Account/{membership_id}/"
2349            f"Character/{character_id}/Stats/AggregateActivityStats/",
2350        )
2351        assert isinstance(resp, dict)
2352        return resp

Fetch aggregated activity stats for a specific membership character.

Parameters
  • character_id (int): The character ID to return the stats for.
  • membership_id (int): The Destiny membership id to return the stats for.
  • membership_type (aiobungie.MembershipType | int): The Destiny membership type to return the stats for.
Returns
class RESTPool:
242class RESTPool:
243    """Pool of `RESTClient` instances.
244
245    This allows to create multiple instances of `RESTClient`s that can be acquired
246    which share the same config and metadata.
247
248    Example
249    -------
250    ```py
251    import aiobungie
252    import asyncio
253
254    client_pool = aiobungie.RESTPool("token", client_id=1234, client_secret='secret')
255
256    # Using a context manager to acquire an instance
257    # of the pool and close the connection after finishing.
258
259    async def first() -> str:
260        async with client_pool.acquire() as client:
261            return client.build_oauth2_url()
262
263    async def second() -> None:
264        async with client_pool.acquire() as client:
265            new_tokens = await client.refresh_access_token("token")
266            client.metadata['tokens'] = new_tokens
267
268    # Client instances are independent from first and second.
269    await asyncio.gather(first(), second())
270    ```
271
272    Parameters
273    ----------
274    token : `str`
275        A valid application token from Bungie's developer portal.
276
277    Other Parameters
278    ----------------
279    max_retries : `int`
280        The max retries number to retry if the request hit a `5xx` status code.
281    max_ratelimit_retries : `int`
282        The max retries number to retry if the request hit a `429` status code. Defaults to `3`.
283    client_secret : `typing.Optional[str]`
284        An optional application client secret,
285        This is only needed if you're fetching OAuth2 tokens with this client.
286    client_id : `typing.Optional[int]`
287        An optional application client id,
288        This is only needed if you're fetching OAuth2 tokens with this client.
289    enable_debugging : `bool | str`
290        Whether to enable logging responses or not.
291
292    Logging Levels
293    --------------
294    * `False`: This will disable logging.
295    * `True`: This will set the level to `DEBUG` and enable logging minimal information.
296    Like the response status, route, taken time and so on.
297    * `"TRACE" | aiobungie.TRACE`: This will log the response headers along with the minimal information.
298    """
299
300    __slots__ = (
301        "_token",
302        "_max_retries",
303        "_client_secret",
304        "_client_id",
305        "_max_rate_limit_retries",
306        "_metadata",
307        "_enable_debug",
308    )
309
310    # Looks like mypy doesn't like this.
311    if typing.TYPE_CHECKING:
312        _enable_debug: typing.Union[typing.Literal["TRACE"], bool, int]
313
314    def __init__(
315        self,
316        token: str,
317        /,
318        client_secret: typing.Optional[str] = None,
319        client_id: typing.Optional[int] = None,
320        *,
321        max_retries: int = 4,
322        max_rate_limit_retries: int = 3,
323        enable_debugging: typing.Union[typing.Literal["TRACE"], bool, int] = False,
324    ) -> None:
325        self._client_secret = client_secret
326        self._client_id = client_id
327        self._token: str = token
328        self._max_retries = max_retries
329        self._max_rate_limit_retries = max_rate_limit_retries
330        self._metadata: collections.MutableMapping[typing.Any, typing.Any] = {}
331        self._enable_debug = enable_debugging
332
333    @property
334    def client_id(self) -> typing.Optional[int]:
335        return self._client_id
336
337    @property
338    def metadata(self) -> collections.MutableMapping[typing.Any, typing.Any]:
339        """Pool's Metadata. This is different from client instance metadata."""
340        return self._metadata
341
342    @typing.final
343    def acquire(self) -> RESTClient:
344        """Acquires a new `RESTClient` instance from this REST pool.
345
346        Returns
347        -------
348        `RESTClient`
349            An instance of a REST client.
350        """
351        instance = RESTClient(
352            self._token,
353            client_secret=self._client_secret,
354            client_id=self._client_id,
355            max_retries=self._max_retries,
356            max_ratelimit_retries=self._max_rate_limit_retries,
357            enable_debugging=self._enable_debug,
358        )
359        return instance

Pool of RESTClient instances.

This allows to create multiple instances of RESTClients that can be acquired which share the same config and metadata.

Example
import aiobungie
import asyncio

client_pool = aiobungie.RESTPool("token", client_id=1234, client_secret='secret')

# Using a context manager to acquire an instance
# of the pool and close the connection after finishing.

async def first() -> str:
    async with client_pool.acquire() as client:
        return client.build_oauth2_url()

async def second() -> None:
    async with client_pool.acquire() as client:
        new_tokens = await client.refresh_access_token("token")
        client.metadata['tokens'] = new_tokens

# Client instances are independent from first and second.
await asyncio.gather(first(), second())
Parameters
  • token (str): A valid application token from Bungie's developer portal.
Other Parameters
  • max_retries (int): The max retries number to retry if the request hit a 5xx status code.
  • max_ratelimit_retries (int): The max retries number to retry if the request hit a 429 status code. Defaults to 3.
  • client_secret (typing.Optional[str]): An optional application client secret, This is only needed if you're fetching OAuth2 tokens with this client.
  • client_id (typing.Optional[int]): An optional application client id, This is only needed if you're fetching OAuth2 tokens with this client.
  • enable_debugging (bool | str): Whether to enable logging responses or not.
Logging Levels
  • False: This will disable logging.
  • True: This will set the level to DEBUG and enable logging minimal information. Like the response status, route, taken time and so on.
  • "TRACE" | aiobungie.TRACE: This will log the response headers along with the minimal information.
RESTPool( token: str, /, client_secret: Optional[str] = None, client_id: Optional[int] = None, *, max_retries: int = 4, max_rate_limit_retries: int = 3, enable_debugging: Union[Literal['TRACE'], bool, int] = False)
314    def __init__(
315        self,
316        token: str,
317        /,
318        client_secret: typing.Optional[str] = None,
319        client_id: typing.Optional[int] = None,
320        *,
321        max_retries: int = 4,
322        max_rate_limit_retries: int = 3,
323        enable_debugging: typing.Union[typing.Literal["TRACE"], bool, int] = False,
324    ) -> None:
325        self._client_secret = client_secret
326        self._client_id = client_id
327        self._token: str = token
328        self._max_retries = max_retries
329        self._max_rate_limit_retries = max_rate_limit_retries
330        self._metadata: collections.MutableMapping[typing.Any, typing.Any] = {}
331        self._enable_debug = enable_debugging
metadata: collections.abc.MutableMapping[typing.Any, typing.Any]

Pool's Metadata. This is different from client instance metadata.

@typing.final
def acquire(self) -> aiobungie.RESTClient:
342    @typing.final
343    def acquire(self) -> RESTClient:
344        """Acquires a new `RESTClient` instance from this REST pool.
345
346        Returns
347        -------
348        `RESTClient`
349            An instance of a REST client.
350        """
351        instance = RESTClient(
352            self._token,
353            client_secret=self._client_secret,
354            client_id=self._client_id,
355            max_retries=self._max_retries,
356            max_ratelimit_retries=self._max_rate_limit_retries,
357            enable_debugging=self._enable_debug,
358        )
359        return instance

Acquires a new RESTClient instance from this REST pool.

Returns
@typing.final
class Race(builtins.int, aiobungie.Enum):
493@typing.final
494class Race(int, Enum):
495    """An Enum for Destiny races."""
496
497    HUMAN = 0
498    AWOKEN = 1
499    EXO = 2
500    UNKNOWN = 3

An Enum for Destiny races.

HUMAN = <Race.HUMAN: 0>
AWOKEN = <Race.AWOKEN: 1>
EXO = <Race.EXO: 2>
UNKNOWN = <Race.UNKNOWN: 3>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Raid(builtins.int, aiobungie.Enum):
143@typing.final
144class Raid(int, Enum):
145    """An Enum for all available raids in Destiny 2."""
146
147    DSC = 910380154
148    """Deep Stone Crypt"""
149
150    LW = 2122313384
151    """Last Wish"""
152
153    VOG = 3881495763
154    """Normal Valut of Glass"""
155
156    GOS = 3458480158
157    """Garden Of Salvation"""

An Enum for all available raids in Destiny 2.

DSC = <Raid.DSC: 910380154>

Deep Stone Crypt

LW = <Raid.LW: 2122313384>

Last Wish

VOG = <Raid.VOG: 3881495763>

Normal Valut of Glass

GOS = <Raid.GOS: 3458480158>

Garden Of Salvation

Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@attrs.define(auto_exc=True)
class RateLimitedError(aiobungie.HTTPError):
200@attrs.define(auto_exc=True)
201class RateLimitedError(HTTPError):
202    """Raised when being hit with ratelimits."""
203
204    http_status: http.HTTPStatus = attrs.field(
205        default=http.HTTPStatus.TOO_MANY_REQUESTS, init=False
206    )
207    """The request response http status."""
208
209    url: typedefs.StrOrURL
210    """The URL/endpoint caused this error."""
211
212    body: typing.Any
213    """The response body."""
214
215    retry_after: float = attrs.field(default=0.0)
216    """The amount of seconds you need to wait before retrying to requests."""
217
218    message: str = attrs.field(init=False)
219    """A Bungie human readable message describes the cause of the error."""
220
221    @message.default  # type: ignore
222    def _(self) -> str:
223        return f"You're ratelimited for {self.retry_after}, Endpoint: {self.url}. Slow down!"
224
225    def __str__(self) -> str:
226        return self.message

Raised when being hit with ratelimits.

RateLimitedError(url: Union[str, yarl.URL], body: Any, retry_after: float = 0.0)
2def __init__(self, url, body, retry_after=attr_dict['retry_after'].default):
3    self.http_status = attr_dict['http_status'].default
4    self.url = url
5    self.body = body
6    self.retry_after = retry_after
7    self.message = __attr_factory_message(self)
8    BaseException.__init__(self, self.url,self.body,self.retry_after)

Method generated by attrs for class RateLimitedError.

http_status: http.HTTPStatus

The request response http status.

url: Union[str, yarl.URL]

The URL/endpoint caused this error.

body: Any

The response body.

retry_after: float

The amount of seconds you need to wait before retrying to requests.

message: str

A Bungie human readable message describes the cause of the error.

Inherited Members
builtins.BaseException
with_traceback
@typing.final
class RecordState(aiobungie.Flag):
48@typing.final
49class RecordState(enums.Flag):
50    """An enum for records component states."""
51
52    NONE = 0
53    REDEEMED = 1 << 0
54    UNAVAILABLE = 1 << 1
55    OBJECTIVE_NOT_COMPLETED = 1 << 2
56    OBSCURED = 1 << 3
57    INVISIBLE = 1 << 4
58    ENTITLEMENT_UNOWNED = 1 << 5
59    CAN_EQUIP_TITLE = 1 << 6

An enum for records component states.

NONE = <RecordState.NONE: 0>
REDEEMED = <RecordState.REDEEMED: 1>
UNAVAILABLE = <RecordState.UNAVAILABLE: 2>
OBJECTIVE_NOT_COMPLETED = <RecordState.OBJECTIVE_NOT_COMPLETED: 4>
OBSCURED = <RecordState.OBSCURED: 8>
INVISIBLE = <RecordState.INVISIBLE: 16>
ENTITLEMENT_UNOWNED = <RecordState.ENTITLEMENT_UNOWNED: 32>
CAN_EQUIP_TITLE = <RecordState.CAN_EQUIP_TITLE: 64>
Inherited Members
Flag
name
value
@typing.final
class Relationship(builtins.int, aiobungie.Enum):
688@typing.final
689class Relationship(int, Enum):
690    """An enum for bungie friends relationship types."""
691
692    UNKNOWN = 0
693    FRIEND = 1
694    INCOMING_REQUEST = 2
695    OUTGOING_REQUEST = 3

An enum for bungie friends relationship types.

UNKNOWN = <Relationship.UNKNOWN: 0>
FRIEND = <Relationship.FRIEND: 1>
INCOMING_REQUEST = <Relationship.INCOMING_REQUEST: 2>
OUTGOING_REQUEST = <Relationship.OUTGOING_REQUEST: 3>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
class RequestMethod(builtins.str, aiobungie.Enum):
223class RequestMethod(str, enums.Enum):
224    """HTTP request methods enum."""
225
226    GET = "GET"
227    """GET methods."""
228    POST = "POST"
229    """POST methods."""
230    PUT = "PUT"
231    """PUT methods."""
232    PATCH = "PATCH"
233    """PATCH methods."""
234    DELETE = "DELETE"
235    """DELETE methods"""

HTTP request methods enum.

GET = <RequestMethod.GET: GET>

GET methods.

POST = <RequestMethod.POST: POST>

POST methods.

PUT = <RequestMethod.PUT: PUT>

PUT methods.

PATCH = <RequestMethod.PATCH: PATCH>

PATCH methods.

DELETE = <RequestMethod.DELETE: DELETE>

DELETE methods

Inherited Members
Enum
name
value
builtins.str
encode
replace
split
rsplit
join
capitalize
casefold
title
center
count
expandtabs
find
partition
index
ljust
lower
lstrip
rfind
rindex
rjust
rstrip
rpartition
splitlines
strip
swapcase
translate
upper
startswith
endswith
removeprefix
removesuffix
isascii
islower
isupper
istitle
isspace
isdecimal
isdigit
isnumeric
isalpha
isalnum
isidentifier
isprintable
zfill
format
format_map
maketrans
@attrs.define(auto_exc=True)
class ResponseError(aiobungie.HTTPException):
195@attrs.define(auto_exc=True)
196class ResponseError(HTTPException):
197    """Standard HTTP responses exception."""

Standard HTTP responses exception.

ResponseError( *, error_code: int, http_status: http.HTTPStatus, throttle_seconds: int, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str], message: str, error_status: str, message_data: dict[str, str])
 2def __init__(self, *, error_code, http_status, throttle_seconds, url, body, headers, message, error_status, message_data):
 3    self.error_code = error_code
 4    self.http_status = http_status
 5    self.throttle_seconds = throttle_seconds
 6    self.url = url
 7    self.body = body
 8    self.headers = headers
 9    self.message = message
10    self.error_status = error_status
11    self.message_data = message_data
12    BaseException.__init__(self, self.error_code,self.http_status,self.throttle_seconds,self.url,self.body,self.headers,self.message,self.error_status,self.message_data)

Method generated by attrs for class ResponseError.

Inherited Members
HTTPException
error_code
http_status
throttle_seconds
url
body
headers
message
error_status
message_data
builtins.BaseException
with_traceback
@typing.final
class Stat(builtins.int, aiobungie.Enum):
515@typing.final
516class Stat(int, Enum):
517    """An Enum for Destiny 2 character stats."""
518
519    NONE = 0
520    MOBILITY = 2996146975
521    RESILIENCE = 392767087
522    RECOVERY = 1943323491
523    DISCIPLINE = 1735777505
524    INTELLECT = 144602215
525    STRENGTH = 4244567218
526    LIGHT_POWER = 1935470627

An Enum for Destiny 2 character stats.

NONE = <Stat.NONE: 0>
MOBILITY = <Stat.MOBILITY: 2996146975>
RESILIENCE = <Stat.RESILIENCE: 392767087>
RECOVERY = <Stat.RECOVERY: 1943323491>
DISCIPLINE = <Stat.DISCIPLINE: 1735777505>
INTELLECT = <Stat.INTELLECT: 144602215>
STRENGTH = <Stat.STRENGTH: 4244567218>
LIGHT_POWER = <Stat.LIGHT_POWER: 1935470627>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
TRACE = 5
@typing.final
class TierType(builtins.int, aiobungie.Enum):
630@typing.final
631class TierType(int, Enum):
632    """An enum for a Destiny 2 item tier type."""
633
634    UNKNOWN = 0
635    CURRENCY = 1
636    BASIC = 2
637    COMMON = 3
638    RARE = 4
639    SUPERIOR = 5
640    EXOTIC = 6

An enum for a Destiny 2 item tier type.

UNKNOWN = <TierType.UNKNOWN: 0>
CURRENCY = <TierType.CURRENCY: 1>
BASIC = <TierType.BASIC: 2>
COMMON = <TierType.COMMON: 3>
RARE = <TierType.RARE: 4>
SUPERIOR = <TierType.SUPERIOR: 5>
EXOTIC = <TierType.EXOTIC: 6>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class TransferStatus(aiobungie.Flag):
740@typing.final
741class TransferStatus(Flag):
742    """An enum for items transfer statuses."""
743
744    CAN_TRANSFER = 0
745    """The item can be transferred."""
746    IS_EQUIPPED = 1 << 0
747    """You can't transfer since the item is equipped."""
748    NOT_TRASNFERRABLE = 1 << 1
749    """This item can not be transferred."""
750    COULD_BE_TRANSFERRED = 1 << 2
751    """You can trasnfer the item. But the place you're trying to put it at has no space for it."""

An enum for items transfer statuses.

CAN_TRANSFER = <TransferStatus.CAN_TRANSFER: 0>

The item can be transferred.

IS_EQUIPPED = <TransferStatus.IS_EQUIPPED: 1>

You can't transfer since the item is equipped.

NOT_TRASNFERRABLE = <TransferStatus.NOT_TRASNFERRABLE: 2>

This item can not be transferred.

COULD_BE_TRANSFERRED = <TransferStatus.COULD_BE_TRANSFERRED: 4>

You can trasnfer the item. But the place you're trying to put it at has no space for it.

Inherited Members
Flag
name
value
UNDEFINED = UNDEFINED
@attrs.define(auto_exc=True)
class Unauthorized(aiobungie.HTTPException):
138@attrs.define(auto_exc=True)
139class Unauthorized(HTTPException):
140    """Unauthorized access."""
141
142    http_status: http.HTTPStatus = attrs.field(
143        default=http.HTTPStatus.UNAUTHORIZED, init=False
144    )

Unauthorized access.

Unauthorized( *, error_code: int, throttle_seconds: int, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str], message: str, error_status: str, message_data: dict[str, str])
 2def __init__(self, *, error_code, throttle_seconds, url, body, headers, message, error_status, message_data):
 3    self.error_code = error_code
 4    self.throttle_seconds = throttle_seconds
 5    self.url = url
 6    self.body = body
 7    self.headers = headers
 8    self.message = message
 9    self.error_status = error_status
10    self.message_data = message_data
11    self.http_status = attr_dict['http_status'].default
12    BaseException.__init__(self, self.error_code,self.throttle_seconds,self.url,self.body,self.headers,self.message,self.error_status,self.message_data)

Method generated by attrs for class Unauthorized.

http_status: http.HTTPStatus

The request response http status.

Inherited Members
HTTPException
error_code
throttle_seconds
url
body
headers
message
error_status
message_data
builtins.BaseException
with_traceback
UndefinedOr = typing.Union[aiobungie.UndefinedType, +_T]
class UndefinedType:
33class UndefinedType:
34    """An `UNDEFINED` type."""
35
36    __instance: typing.Optional[UndefinedType] = None
37
38    def __bool__(self) -> typing.Literal[False]:
39        return False
40
41    def __int__(self) -> typing.Literal[0]:
42        return 0
43
44    def __repr__(self) -> str:
45        return "UNDEFINED"
46
47    def __str__(self) -> str:
48        return "UNDEFINED"
49
50    def __new__(cls) -> UndefinedType:
51        if cls.__instance is None:
52            o = super().__new__(cls)
53            cls.__instance = o
54        return cls.__instance

An UNDEFINED type.

UndefinedType()
@typing.final
class ValueUIStyle(builtins.int, aiobungie.Enum):
75@typing.final
76class ValueUIStyle(int, enums.Enum):
77    AUTOMATIC = 0
78    FRACTION = 1
79    CHECK_BOX = 2
80    PERCENTAGE = 3
81    DATETIME = 4
82    FRACTION_FLOAT = 5
83    INTEGER = 6
84    TIME_DURATION = 7
85    HIDDEN = 8
86    MULTIPLIER = 9
87    GREEN_PIPS = 10
88    RED_PIPS = 11
89    EXPLICIT_PERCENTAGE = 12
90    RAW_FLOAT = 13
91    LEVEL_AND_REWARD = 14

An enumeration.

AUTOMATIC = <ValueUIStyle.AUTOMATIC: 0>
FRACTION = <ValueUIStyle.FRACTION: 1>
CHECK_BOX = <ValueUIStyle.CHECK_BOX: 2>
PERCENTAGE = <ValueUIStyle.PERCENTAGE: 3>
DATETIME = <ValueUIStyle.DATETIME: 4>
FRACTION_FLOAT = <ValueUIStyle.FRACTION_FLOAT: 5>
INTEGER = <ValueUIStyle.INTEGER: 6>
TIME_DURATION = <ValueUIStyle.TIME_DURATION: 7>
HIDDEN = <ValueUIStyle.HIDDEN: 8>
MULTIPLIER = <ValueUIStyle.MULTIPLIER: 9>
GREEN_PIPS = <ValueUIStyle.GREEN_PIPS: 10>
RED_PIPS = <ValueUIStyle.RED_PIPS: 11>
EXPLICIT_PERCENTAGE = <ValueUIStyle.EXPLICIT_PERCENTAGE: 12>
RAW_FLOAT = <ValueUIStyle.RAW_FLOAT: 13>
LEVEL_AND_REWARD = <ValueUIStyle.LEVEL_AND_REWARD: 14>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Vendor(builtins.int, aiobungie.Enum):
240@typing.final
241class Vendor(int, Enum):
242    """An Enum for all available vendors in Destiny 2."""
243
244    ZAVALA = 69482069
245    XUR = 2190858386
246    BANSHE = 672118013
247    SPIDER = 863940356
248    SHAXX = 3603221665
249    KADI = 529635856
250    """Postmaster exo."""
251    YUNA = 1796504621
252    """Asia servers only."""
253    EVERVERSE = 3361454721
254    AMANDA = 460529231
255    """Amanda holiday"""
256    CROW = 3611983588
257    HAWTHORNE = 3347378076
258    ADA1 = 350061650
259    DRIFTER = 248695599
260    IKORA = 1976548992
261    SAINT = 765357505
262    """Saint-14"""
263    ERIS_MORN = 1616085565
264    SHAW_HAWN = 1816541247
265    """COSMODROME Guy"""
266    VARIKS = 2531198101

An Enum for all available vendors in Destiny 2.

ZAVALA = <Vendor.ZAVALA: 69482069>
XUR = <Vendor.XUR: 2190858386>
BANSHE = <Vendor.BANSHE: 672118013>
SPIDER = <Vendor.SPIDER: 863940356>
SHAXX = <Vendor.SHAXX: 3603221665>
KADI = <Vendor.KADI: 529635856>

Postmaster exo.

YUNA = <Vendor.YUNA: 1796504621>

Asia servers only.

EVERVERSE = <Vendor.EVERVERSE: 3361454721>
AMANDA = <Vendor.AMANDA: 460529231>

Amanda holiday

CROW = <Vendor.CROW: 3611983588>
HAWTHORNE = <Vendor.HAWTHORNE: 3347378076>
ADA1 = <Vendor.ADA1: 350061650>
DRIFTER = <Vendor.DRIFTER: 248695599>
IKORA = <Vendor.IKORA: 1976548992>
SAINT = <Vendor.SAINT: 765357505>

Saint-14

ERIS_MORN = <Vendor.ERIS_MORN: 1616085565>
SHAW_HAWN = <Vendor.SHAW_HAWN: 1816541247>

COSMODROME Guy

VARIKS = <Vendor.VARIKS: 2531198101>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class WeaponType(builtins.int, aiobungie.Enum):
529@typing.final
530class WeaponType(int, Enum):
531    """Enums for The three Destiny Weapon Types"""
532
533    NONE = 0
534    KINETIC = 1498876634
535    ENERGY = 2465295065
536    POWER = 953998645

Enums for The three Destiny Weapon Types

NONE = <WeaponType.NONE: 0>
KINETIC = <WeaponType.KINETIC: 1498876634>
ENERGY = <WeaponType.ENERGY: 2465295065>
POWER = <WeaponType.POWER: 953998645>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
annotations = _Feature((3, 7, 0, 'beta', 1), (3, 11, 0, 'alpha', 0), 16777216)
def into_iter( iterable: collections.abc.Iterable[~Item]) -> aiobungie.Iterator[~Item]:
559def into_iter(
560    iterable: collections.Iterable[Item],
561) -> Iterator[Item]:
562    """Transform an iterable into an flat iterator.
563
564    Example
565    -------
566    ```py
567    sequence = [1,2,3]
568    for item in aiobungie.into_iter(sequence).reversed():
569        print(item)
570    # 3
571    # 2
572    # 1
573    ```
574
575    Parameters
576    ----------
577    iterable: `typing.Iterable[Item]`
578        The iterable to convert.
579
580    Raises
581    ------
582    `StopIteration`
583        If no elements are left in the iterator.
584    """
585    return Iterator(iterable)

Transform an iterable into an flat iterator.

Example
sequence = [1,2,3]
for item in aiobungie.into_iter(sequence).reversed():
    print(item)
# 3
# 2
# 1
Parameters
  • iterable (typing.Iterable[Item]): The iterable to convert.
Raises
  • StopIteration: If no elements are left in the iterator.
async def raise_error( response: aiohttp.client_reqrep.ClientResponse) -> aiobungie.AiobungieError:
229async def raise_error(response: aiohttp.ClientResponse) -> AiobungieError:
230    """Generates and raise exceptions on error responses."""
231
232    # Not a JSON response, raise immediately.
233
234    # Also Bungie sometimes get funky and return HTML instead of JSON when making an authorized
235    # request with a dummy access token. I can't really do anything about this..
236    if response.content_type != "application/json":
237        return HTTPError(
238            f"Expected JSON content but got {response.content_type!s}, {response.real_url!s}",
239            http.HTTPStatus.UNSUPPORTED_MEDIA_TYPE,
240        )
241
242    body = await response.json()
243    message: str = body.get("Message", "UNDEFINED_MESSAGE")
244    error_status: str = body.get("ErrorStatus", "UNDEFINED_ERROR_STATUS")
245    message_data: dict[str, str] = body.get("MessageData", {})
246    throttle_seconds: int = body.get("ThrottleSeconds", 0)
247    error_code: int = body.get("ErrorCode", 0)
248
249    # Standard HTTP status.
250    if response.status == http.HTTPStatus.NOT_FOUND:
251        return NotFound(
252            message=message,
253            error_code=error_code,
254            throttle_seconds=throttle_seconds,
255            url=str(response.real_url),
256            body=body,
257            headers=response.headers,
258            error_status=error_status,
259            message_data=message_data,
260        )
261
262    elif response.status == http.HTTPStatus.FORBIDDEN:
263        return Forbidden(
264            message=message,
265            error_code=error_code,
266            throttle_seconds=throttle_seconds,
267            url=str(response.real_url),
268            body=body,
269            headers=response.headers,
270            error_status=error_status,
271            message_data=message_data,
272        )
273
274    elif response.status == http.HTTPStatus.UNAUTHORIZED:
275        return Unauthorized(
276            message=message,
277            error_code=error_code,
278            throttle_seconds=throttle_seconds,
279            url=str(response.real_url),
280            body=body,
281            headers=response.headers,
282            error_status=error_status,
283            message_data=message_data,
284        )
285
286    elif response.status == http.HTTPStatus.BAD_REQUEST:
287        # Membership needs to be alone.
288        if error_status == "InvalidParameters":
289            return MembershipTypeError(
290                message=message,
291                body=body,
292                headers=response.headers,
293                url=str(response.url),
294                membership_type=message_data["membershipType"],
295                required_membership=message_data["membershipInfo.membershipType"],
296                membership_id=int(message_data["membershipId"]),
297            )
298        return BadRequest(
299            message=message,
300            body=body,
301            headers=response.headers,
302            url=str(response.url),
303        )
304
305    status = http.HTTPStatus(response.status)
306
307    if 400 <= status < 500:
308        return ResponseError(
309            message=message,
310            error_code=error_code,
311            throttle_seconds=throttle_seconds,
312            url=str(response.real_url),
313            body=body,
314            headers=response.headers,
315            error_status=error_status,
316            message_data=message_data,
317            http_status=status,
318        )
319
320    # Need to self handle ~5xx errors
321    elif 500 <= status < 600:
322        # No API key or method requires OAuth2 most likely.
323        if error_status in {
324            "ApiKeyMissingFromRequest",
325            "WebAuthRequired",
326            "ApiInvalidOrExpiredKey",
327            "AuthenticationInvalid",
328            "AuthorizationCodeInvalid",
329        }:
330            return Unauthorized(
331                message=message,
332                error_code=error_code,
333                throttle_seconds=throttle_seconds,
334                url=str(response.real_url),
335                body=body,
336                headers=response.headers,
337                error_status=error_status,
338                message_data=message_data,
339            )
340
341        # Anything contains not found.
342        elif (
343            "NotFound" in error_status or error_status == "UserCannotFindRequestedUser"
344        ):
345            return NotFound(
346                message=message,
347                error_code=error_code,
348                throttle_seconds=throttle_seconds,
349                url=str(response.real_url),
350                body=body,
351                headers=response.headers,
352                error_status=error_status,
353                message_data=message_data,
354            )
355
356        # Other 5xx errors.
357        else:
358            return InternalServerError(
359                message=message,
360                error_code=error_code,
361                throttle_seconds=throttle_seconds,
362                url=str(response.real_url),
363                body=body,
364                headers=response.headers,
365                error_status=error_status,
366                message_data=message_data,
367                http_status=status,
368            )
369    # Something else.
370    else:
371        return HTTPException(
372            message=message,
373            error_code=error_code,
374            throttle_seconds=throttle_seconds,
375            url=str(response.real_url),
376            body=body,
377            headers=response.headers,
378            error_status=error_status,
379            message_data=message_data,
380            http_status=status,
381        )

Generates and raise exceptions on error responses.

def stringify_http_message(headers: 'collections.Mapping[str, str]') -> str:
384def stringify_http_message(headers: collections.Mapping[str, str]) -> str:
385    return (
386        "{ \n"
387        + "\n".join(  # noqa: W503
388            f"{f'   {key}'}: {value}"
389            if key not in ("Authorization", "X-API-KEY")
390            else f"   {key}: HIDDEN_TOKEN"
391            for key, value in headers.items()
392        )
393        + "\n}"  # noqa: W503
394    )